def _generate_experiment_programs( tomo_experiment: TomographyExperiment, active_reset: bool = False ) -> Tuple[List[Program], List[List[int]]]: """ Generate the programs necessary to estimate the observables in a TomographyExperiment. Grouping of settings to be run in parallel, e.g. by a call to group_experiments, should be done before this function is called. .. CAUTION:: One must be careful with compilation of the output programs before the appropriate MEASURE instructions are added, because compilation may re-index the qubits so that the output list of `measure_qubits` no longer accurately indexes the qubits that should be measured. :param tomo_experiment: a single TomographyExperiment to be translated to a series of programs that, when run serially, can be used to estimate each of its observables. :param active_reset: whether or not to begin the program by actively resetting. If true, execution of each of the returned programs in a loop on the QPU will generally be faster. :return: a list of programs along with a corresponding list of the groups of qubits that are measured by that program. The returned programs may be run on a qc after measurement instructions are added for the corresponding group of qubits in meas_qubits, or by a call to `qc.run_symmetrized_readout` -- see :func:`raw_estimate_observables` for possible usage. """ # Outer loop over a collection of grouped settings for which we can simultaneously estimate. programs = [] meas_qubits = [] for settings in tomo_experiment: # Prepare a state according to the amalgam of all setting.in_state total_prog = Program() if active_reset: total_prog += RESET() max_weight_in_state = _max_weight_state(setting.in_state for setting in settings) if max_weight_in_state is None: raise ValueError( "Input states are not compatible. Re-group the experiment settings " "so that groups of parallel settings have compatible input states." ) for oneq_state in max_weight_in_state.states: total_prog += _one_q_state_prep(oneq_state) # Add in the program total_prog += tomo_experiment.program # Prepare for measurement state according to setting.out_operator max_weight_out_op = _max_weight_operator(setting.out_operator for setting in settings) if max_weight_out_op is None: raise ValueError( "Observables not compatible. Re-group the experiment settings " "so that groups of parallel settings have compatible observables." ) for qubit, op_str in max_weight_out_op: total_prog += _local_pauli_eig_meas(op_str, qubit) programs.append(total_prog) meas_qubits.append(max_weight_out_op.get_qubits()) return programs, meas_qubits
def _expt_settings_diagonal_in_tpb(es1: ExperimentSetting, es2: ExperimentSetting): """ Extends the concept of being diagonal in the same tpb to ExperimentSettings, by determining if the pairs of in_states and out_operators are separately diagonal in the same tpb """ max_weight_in = _max_weight_state([es1.in_state, es2.in_state]) max_weight_out = _max_weight_operator( [es1.out_operator, es2.out_operator]) return max_weight_in is not None and max_weight_out is not None
def test_expt_settings_share_ntpb(): expts = [ [ ExperimentSetting(zeros_state([0, 1]), sX(0) * sI(1)), ExperimentSetting(zeros_state([0, 1]), sI(0) * sX(1)), ], [ ExperimentSetting(zeros_state([0, 1]), sZ(0) * sI(1)), ExperimentSetting(zeros_state([0, 1]), sI(0) * sZ(1)), ], ] for group in expts: for e1, e2 in itertools.combinations(group, 2): assert _max_weight_state([e1.in_state, e2.in_state]) is not None assert _max_weight_operator([e1.out_operator, e2.out_operator]) is not None
def test_max_weight_operator_4(): # this last example illustrates that a pair of commuting operators # need not be diagonal in the same tpb assert _max_weight_operator([sX(1) * sZ(0), sZ(1) * sX(0)]) is None
def test_max_weight_operator_misc(): assert _max_weight_operator([sZ(0), sZ(0) * sZ(1)]) is not None assert _max_weight_operator([sX(5), sZ(4)]) is not None assert _max_weight_operator([sX(0), sY(0) * sZ(2)]) is None x_term = sX(0) * sX(1) z1_term = sZ(1) z0_term = sZ(0) z0z1_term = sZ(0) * sZ(1) assert _max_weight_operator([x_term, z1_term]) is None assert _max_weight_operator([z0z1_term, x_term]) is None assert _max_weight_operator([z1_term, z0_term]) is not None assert _max_weight_operator([z0z1_term, z0_term]) is not None assert _max_weight_operator([z0z1_term, z1_term]) is not None assert _max_weight_operator([z0z1_term, sI(1)]) is not None assert _max_weight_operator([z0z1_term, sI(2)]) is not None assert _max_weight_operator([z0z1_term, sX(5) * sZ(7)]) is not None xxxx_terms = (sX(1) * sX(2) + sX(2) + sX(3) * sX(4) + sX(4) + sX(1) * sX(3) * sX(4) + sX(1) * sX(4) + sX(1) * sX(2) * sX(3)) true_term = sX(1) * sX(2) * sX(3) * sX(4) assert _max_weight_operator(xxxx_terms.terms) == true_term zzzz_terms = sZ(1) * sZ(2) + sZ(3) * sZ(4) + sZ(1) * sZ(3) + sZ(1) * sZ( 3) * sZ(4) assert _max_weight_operator( zzzz_terms.terms) == sZ(1) * sZ(2) * sZ(3) * sZ(4) pauli_terms = [sZ(0), sX(1) * sZ(0), sY(2) * sX(1), sZ(5) * sI(3)] assert _max_weight_operator(pauli_terms) == sZ(5) * sY(2) * sX(1) * sZ(0)
def test_max_weight_operator_3(): pauli_terms = [sZ(0) * sX(5), sX(1) * sZ(0), sY(2) * sX(1), sZ(5) * sI(3)] assert _max_weight_operator(pauli_terms) is None
def test_max_weight_operator_1(): pauli_terms = [sZ(0), sX(1) * sZ(0), sY(2) * sX(1)] assert _max_weight_operator(pauli_terms) == sY(2) * sX(1) * sZ(0)