Example #1
0
def group_settings_greedy(
    settings: Iterable[InitObsSetting],
) -> Dict[InitObsSetting, List[InitObsSetting]]:
    """Greedily group settings which can be simultaneously measured.

    We construct a dictionary keyed by `max_setting` (see docstrings
    for `_max_weight_state` and `_max_weight_observable`) where the value
    is a list of settings compatible with `max_setting`. For each new setting,
    we try to find an existing group to add it and update `max_setting` for
    that group if necessary. Otherwise, we make a new group.

    In practice, this greedy algorithm performs comparably to something
    more complicated by solving the clique cover problem on a graph
    of simultaneously-measurable settings.

    Args:
        settings: The settings to group.

    Returns:
        A dictionary keyed by `max_setting` which need not exist in the
        input list of settings. Each dictionary value is a list of
        settings compatible with `max_setting`.
    """
    grouped_settings: Dict[InitObsSetting, List[InitObsSetting]] = {}
    for setting in settings:
        for max_setting, simul_settings in grouped_settings.items():
            trial_grouped_settings = simul_settings + [setting]
            new_max_weight_state = _max_weight_state(
                stg.init_state for stg in trial_grouped_settings)
            new_max_weight_obs = _max_weight_observable(
                stg.observable for stg in trial_grouped_settings)
            compatible_init_state = new_max_weight_state is not None
            compatible_observable = new_max_weight_obs is not None
            can_be_inserted = compatible_init_state and compatible_observable
            if can_be_inserted:
                new_max_weight_state = cast(value.ProductState,
                                            new_max_weight_state)
                new_max_weight_obs = cast(ops.PauliString, new_max_weight_obs)
                del grouped_settings[max_setting]
                new_max_setting = InitObsSetting(new_max_weight_state,
                                                 new_max_weight_obs)
                grouped_settings[new_max_setting] = trial_grouped_settings
                break

        else:
            # made it through entire dict without finding a compatible group,
            # thus a new group needs to be created
            # Strip coefficients before using as key
            new_max_weight_obs = setting.observable.with_coefficient(1.0)
            new_max_setting = InitObsSetting(setting.init_state,
                                             new_max_weight_obs)
            grouped_settings[new_max_setting] = [setting]

    return grouped_settings
def _setting_to_z_observable(setting: InitObsSetting):
    qubits = setting.observable.qubits
    return InitObsSetting(
        init_state=zeros_state(qubits),
        observable=ops.PauliString(qubit_pauli_map={q: ops.Z
                                                    for q in qubits}),
    )
def calibrate_readout_error(
    qubits: Iterable[ops.Qid],
    sampler: Union['cirq.Simulator', 'cirq.Sampler'],
    stopping_criteria: StoppingCriteria,
):
    # We know there won't be any fancy sweeps or observables so we can
    # get away with more repetitions per job
    stopping_criteria = dataclasses.replace(stopping_criteria,
                                            repetitions_per_chunk=100_000)

    # Simultaneous readout characterization:
    # We can measure all qubits simultaneously (i.e. _max_setting is ZZZ..ZZ
    # for all qubits). We will extract individual qubit quantities, so there
    # are `n_qubits` InitObsSetting, each responsible for one <Z>.
    #
    # Readout symmetrization means we just need to measure the "identity"
    # circuit. In reality, this corresponds to measuring I for half the time
    # and X for the other half.
    init_state = zeros_state(qubits)
    max_setting = InitObsSetting(init_state=init_state,
                                 observable=ops.PauliString(
                                     {q: ops.Z
                                      for q in qubits}))
    grouped_settings = {
        max_setting: [
            InitObsSetting(init_state=init_state,
                           observable=ops.PauliString({q: ops.Z}))
            for q in qubits
        ]
    }

    results = measure_grouped_settings(
        circuit=circuits.Circuit(),
        grouped_settings=grouped_settings,
        sampler=sampler,
        stopping_criteria=stopping_criteria,
        circuit_sweep=study.UnitSweep,
        readout_symmetrization=True,
    )
    (result, ) = list(results)
    return result
Example #4
0
def _pad_setting(
    max_setting: InitObsSetting,
    qubits: List['cirq.Qid'],
    pad_init_state_with=value.KET_ZERO,
    pad_obs_with: 'cirq.Gate' = ops.Z,
) -> InitObsSetting:
    """Pad `max_setting`'s `init_state` and `observable` with `pad_xx_with` operations
    (defaults:  |0> and Z) so each max_setting has the same qubits. We need this
    to be the case so we can fill in all the parameters, see `_get_params_for_setting`.
    """
    obs = max_setting.observable
    assert obs.coefficient == 1, "Only the max_setting should be padded."
    for qubit in qubits:
        if not qubit in obs:
            obs *= pad_obs_with(qubit)

    init_state = max_setting.init_state
    init_state_original_qubits = init_state.qubits
    for qubit in qubits:
        if not qubit in init_state_original_qubits:
            init_state *= pad_init_state_with(qubit)

    return InitObsSetting(init_state=init_state, observable=obs)