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
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)