Ejemplo n.º 1
0
def measure_grouped_settings(
    circuit: 'cirq.Circuit',
    grouped_settings: Dict[InitObsSetting, List[InitObsSetting]],
    sampler: 'cirq.Sampler',
    stopping_criteria: StoppingCriteria,
    *,
    readout_symmetrization: bool = False,
    circuit_sweep: 'cirq.study.sweepable.SweepLike' = None,
) -> List[BitstringAccumulator]:
    """Measure a suite of grouped InitObsSetting settings.

    This is a low-level API for accessing the observable measurement
    framework. See also `measure_observables` and `measure_observables_df`.

    Args:
        circuit: The circuit. This can contain parameters, in which case
            you should also specify `circuit_sweep`.
        grouped_settings: A series of setting groups expressed as a dictionary.
            The key is the max-weight setting used for preparing single-qubit
            basis-change rotations. The value is a list of settings
            compatible with the maximal setting you desire to measure.
            Automated routing algorithms like `group_settings_greedy` can
            be used to construct this input.
        sampler: A sampler.
        stopping_criteria: A StoppingCriteria object that can report
            whether enough samples have been sampled.
        readout_symmetrization: If set to True, each `meas_spec` will be
            split into two runs: one normal and one where a bit flip is
            incorporated prior to measurement. In the latter case, the
            measured bit will be flipped back classically and accumulated
            together. This causes readout error to appear symmetric,
            p(0|0) = p(1|1).
        circuit_sweep: Additional parameter sweeps for parameters contained
            in `circuit`. The total sweep is the product of the circuit sweep
            with parameter settings for the single-qubit basis-change rotations.
    """
    qubits = sorted(
        {q
         for ms in grouped_settings.keys() for q in ms.init_state.qubits})
    qubit_to_index = {q: i for i, q in enumerate(qubits)}

    needs_init_layer = _needs_init_layer(grouped_settings)
    measurement_param_circuit = _with_parameterized_layers(
        circuit, qubits, needs_init_layer)
    grouped_settings = {
        _pad_setting(max_setting, qubits): settings
        for max_setting, settings in grouped_settings.items()
    }
    circuit_sweep = study.UnitSweep if circuit_sweep is None else study.to_sweep(
        circuit_sweep)

    # meas_spec provides a key for accumulators.
    # meas_specs_todo is a mutable list. We will pop things from it as various
    # specs are measured to the satisfaction of the stopping criteria
    accumulators = {}
    meas_specs_todo = []
    for max_setting, circuit_params in itertools.product(
            grouped_settings.keys(), circuit_sweep.param_tuples()):
        # The type annotation for Param is just `Iterable`.
        # We make sure that it's truly a tuple.
        circuit_params = dict(circuit_params)

        meas_spec = _MeasurementSpec(max_setting=max_setting,
                                     circuit_params=circuit_params)
        accumulator = BitstringAccumulator(
            meas_spec=meas_spec,
            simul_settings=grouped_settings[max_setting],
            qubit_to_index=qubit_to_index,
        )
        accumulators[meas_spec] = accumulator
        meas_specs_todo += [meas_spec]

    while True:
        meas_specs_todo, repetitions = _check_meas_specs_still_todo(
            meas_specs=meas_specs_todo,
            accumulators=accumulators,
            stopping_criteria=stopping_criteria,
        )
        if len(meas_specs_todo) == 0:
            break

        flippy_meas_specs, repetitions = _subdivide_meas_specs(
            meas_specs=meas_specs_todo,
            repetitions=repetitions,
            qubits=qubits,
            readout_symmetrization=readout_symmetrization,
        )

        resolved_params = [
            flippy_ms.param_tuples(needs_init_layer=needs_init_layer)
            for flippy_ms in flippy_meas_specs
        ]
        resolved_params = _to_sweep(resolved_params)

        results = sampler.run_sweep(program=measurement_param_circuit,
                                    params=resolved_params,
                                    repetitions=repetitions)

        assert len(results) == len(
            flippy_meas_specs
        ), 'Not as many results received as sweeps requested!'

        for flippy_ms, result in zip(flippy_meas_specs, results):
            accumulator = accumulators[flippy_ms.meas_spec]
            bitstrings = np.logical_xor(flippy_ms.flips,
                                        result.measurements['z'])
            accumulator.consume_results(
                bitstrings.astype(np.uint8, casting='safe'))

    return list(accumulators.values())
Ejemplo n.º 2
0
def _to_sweep(param_tuples):
    """Turn param tuples into a sweep."""
    to_sweep = [dict(pt) for pt in param_tuples]
    to_sweep = study.to_sweep(to_sweep)
    return to_sweep