def test_append(): expts = [ [ExperimentSetting(sI(), sX(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sX(1))], [ExperimentSetting(sI(), sZ(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sZ(1))], ] suite = TomographyExperiment( settings=expts, program=Program(X(0), Y(1)), qubits=[0, 1] ) suite.append(ExperimentSetting(sI(), sY(0) * sX(1))) assert (len(str(suite))) > 0
def test_identity(forest): qc = get_qc('2q-qvm') suite = TomographyExperiment([ExperimentSetting(sI(), 0.123 * sI(0))], program=Program(X(0)), qubits=[0]) result = list(measure_observables(qc, suite))[0] assert result.expectation == 0.123
def generate_monte_carlo_process_dfe_experiment(program: Program, qubits: List[int], benchmarker: BenchmarkConnection, n_terms: int = 200) -> TomographyExperiment: """ Estimate process fidelity by randomly sampled direct fidelity estimation. This leads to constant overhead (w.r.t. number of qubits) fidelity estimation. The algorithm is due to: [DFE1] Practical Characterization of Quantum Devices without Tomography Silva et al., PRL 107, 210404 (2011) https://doi.org/10.1103/PhysRevLett.107.210404 [DFE2] Direct Fidelity Estimation from Few Pauli Measurements Flammia and Liu, PRL 106, 230501 (2011) https://doi.org/10.1103/PhysRevLett.106.230501 :param program: A program comprised of Clifford group gates that constructs a state for which we estimate the fidelity. :param qubits: The qubits to perform DFE on. This can be a superset of the qubits used in ``program``. :param benchmarker: The `BenchmarkConnection` object used to design experiments :param n_terms: Number of randomly chosen observables to measure. This number should be a constant less than ``2**len(qubits)``, otherwise ``exhaustive_process_dfe`` is more efficient. :return: a DFE experiment object :rtype: ``DFEExperiment` """ expr = TomographyExperiment(list( _monte_carlo_dfe(program=program, qubits=qubits, in_states=[None, plusX, minusX, plusY, minusY, plusZ, minusZ], n_terms=n_terms, benchmarker=benchmarker)), program=program, qubits=qubits) return expr
def test_experiment_suite_pre_grouped(): expts = [ [ ExperimentSetting(sI(), sX(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sX(1)) ], [ ExperimentSetting(sI(), sZ(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sZ(1)) ], ] suite = TomographyExperiment(settings=expts, program=Program(X(0), Y(1)), qubits=[0, 1]) assert len(suite) == 2 # number of groups for es1, es2 in zip(expts, suite): for e1, e2 in zip(es1, es2): assert e1 == e2 prog_str = str(suite).splitlines()[0] assert prog_str == 'X 0; Y 1'
def test_measure_observables_many_progs(forest): expts = [ ExperimentSetting(sI(), o1 * o2) for o1, o2 in itertools.product([sI(0), sX(0), sY(0), sZ(0)], [sI(1), sX(1), sY(1), sZ(1)]) ] qc = get_qc('2q-qvm') qc.qam.random_seed = 51 for prog in _random_2q_programs(): suite = TomographyExperiment(expts, program=prog, qubits=[0, 1]) assert len(suite) == 4 * 4 gsuite = group_experiments(suite) assert len( gsuite ) == 3 * 3 # can get all the terms with I for free in this case wfn = WavefunctionSimulator() wfn_exps = {} for expt in expts: wfn_exps[expt] = wfn.expectation(gsuite.program, PauliSum([expt.out_operator])) for res in measure_observables(qc, gsuite, n_shots=1_000): np.testing.assert_allclose(wfn_exps[res.setting], res.expectation, atol=0.1)
def test_no_complex_coeffs(forest): qc = get_qc('2q-qvm') suite = TomographyExperiment([ExperimentSetting(sI(), 1.j * sY(0))], program=Program(X(0)), qubits=[0]) with pytest.raises(ValueError): res = list(measure_observables(qc, suite))
def test_max_tpb_overlap_2(): expt_setting = ExperimentSetting(PauliTerm.from_compact_str('(1+0j)*Z7Y8Z1Y4Z2Y5Y0X6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1')) p = Program(H(0), H(1), H(2)) qubits = [0, 1, 2] tomo_expt = TomographyExperiment([expt_setting], p, qubits) expected_dict = {expt_setting: [expt_setting]} assert expected_dict == _max_tpb_overlap(tomo_expt)
def test_group_experiments_greedy(): ungrouped_tomo_expt = TomographyExperiment( [[ExperimentSetting(PauliTerm.from_compact_str('(1+0j)*Z7Y8Z1Y4Z2Y5Y0X6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1'))], [ExperimentSetting(sZ(7), sY(1))]], program=Program(H(0), H(1), H(2)), qubits=[0, 1, 2]) grouped_tomo_expt = group_experiments(ungrouped_tomo_expt, method='greedy') expected_grouped_tomo_expt = TomographyExperiment( [[ ExperimentSetting(TensorProductState.from_str('Z0_7 * Y0_8 * Z0_1 * Y0_4 * ' 'Z0_2 * Y0_5 * Y0_0 * X0_6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1')), ExperimentSetting(plusZ(7), sY(1)) ]], program=Program(H(0), H(1), H(2)), qubits=[0, 1, 2]) assert grouped_tomo_expt == expected_grouped_tomo_expt
def test_group_experiments(grouping_method): expts = [ # cf above, I removed the inner nesting. Still grouped visually ExperimentSetting(sI(), sX(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sX(1)), ExperimentSetting(sI(), sZ(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sZ(1)), ] suite = TomographyExperiment(expts, Program(), qubits=[0, 1]) grouped_suite = group_experiments(suite, method=grouping_method) assert len(suite) == 4 assert len(grouped_suite) == 2
def test_max_tpb_overlap_3(): # add another ExperimentSetting to the above expt_setting = ExperimentSetting(PauliTerm.from_compact_str('(1+0j)*Z7Y8Z1Y4Z2Y5Y0X6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1')) expt_setting2 = ExperimentSetting(sZ(7), sY(1)) p = Program(H(0), H(1), H(2)) qubits = [0, 1, 2] tomo_expt2 = TomographyExperiment([expt_setting, expt_setting2], p, qubits) expected_dict2 = {expt_setting: [expt_setting, expt_setting2]} assert expected_dict2 == _max_tpb_overlap(tomo_expt2)
def test_group_experiments_greedy(): ungrouped_tomo_expt = TomographyExperiment([[ ExperimentSetting( PauliTerm.from_compact_str('(1+0j)*Z7Y8Z1Y4Z2Y5Y0X6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1')) ], [ExperimentSetting(sZ(7), sY(1))]], program=Program( H(0), H(1), H(2)), qubits=[0, 1, 2]) grouped_tomo_expt = group_experiments_greedy(ungrouped_tomo_expt) expected_grouped_tomo_expt = TomographyExperiment([[ ExperimentSetting( PauliTerm.from_compact_str('(1+0j)*Z7Y8Z1Y4Z2Y5Y0X6'), PauliTerm.from_compact_str('(1+0j)*Z4X8Y5X3Y7Y1')), ExperimentSetting(sZ(7), sY(1)) ]], program=Program( H(0), H(1), H(2)), qubits=[0, 1, 2]) assert grouped_tomo_expt == expected_grouped_tomo_expt
def test_measure_observables_no_symm_calibr_raises_error(forest): qc = get_qc('2q-qvm') exptsetting = ExperimentSetting(plusZ(0), sX(0)) suite = TomographyExperiment([exptsetting], program=Program(I(0)), qubits=[0]) with pytest.raises(ValueError): result = list( measure_observables(qc, suite, n_shots=1000, readout_symmetrize=None, calibrate_readout='plus-eig'))
def test_max_tpb_overlap_1(): tomo_expt_settings = [ExperimentSetting(sZ(1) * sX(0), sY(2) * sY(1)), ExperimentSetting(sX(2) * sZ(1), sY(2) * sZ(0))] tomo_expt_program = Program(H(0), H(1), H(2)) tomo_expt_qubits = [0, 1, 2] tomo_expt = TomographyExperiment(tomo_expt_settings, tomo_expt_program, tomo_expt_qubits) expected_dict = { ExperimentSetting(plusX(0) * plusZ(1) * plusX(2), sZ(0) * sY(1) * sY(2)): [ ExperimentSetting(plusZ(1) * plusX(0), sY(2) * sY(1)), ExperimentSetting(plusX(2) * plusZ(1), sY(2) * sZ(0)) ] } assert expected_dict == _max_tpb_overlap(tomo_expt)
def test_sic_process_tomo(forest): qc = get_qc('2q-qvm') process = Program(X(0)) settings = [] for in_state in [SIC0, SIC1, SIC2, SIC3]: for out_op in [sI, sX, sY, sZ]: settings += [ExperimentSetting( in_state=in_state(q=0), out_operator=out_op(q=0) )] experiment = TomographyExperiment(settings=settings, program=process, qubits=[0]) results = list(measure_observables(qc, experiment)) assert len(results) == 4 * 4
def test_experiment_deser(tmpdir): expts = [ [ExperimentSetting(sI(), sX(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sX(1))], [ExperimentSetting(sI(), sZ(0) * sI(1)), ExperimentSetting(sI(), sI(0) * sZ(1))], ] suite = TomographyExperiment( settings=expts, program=Program(X(0), Y(1)), qubits=[0, 1] ) to_json(f'{tmpdir}/suite.json', suite) suite2 = read_json(f'{tmpdir}/suite.json') assert suite == suite2
def test_measure_observables(forest): expts = [ ExperimentSetting(sI(), o1 * o2) for o1, o2 in itertools.product([sI(0), sX(0), sY(0), sZ(0)], [sI(1), sX(1), sY(1), sZ(1)]) ] suite = TomographyExperiment(expts, program=Program(X(0), CNOT(0, 1)), qubits=[0, 1]) assert len(suite) == 4 * 4 gsuite = group_experiments(suite) assert len(gsuite) == 3 * 3 # can get all the terms with I for free in this case qc = get_qc('2q-qvm') for res in measure_observables(qc, gsuite, n_shots=10_000): if res.setting.out_operator in [sI(), sZ(0), sZ(1), sZ(0) * sZ(1)]: assert np.abs(res.expectation) > 0.9 else: assert np.abs(res.expectation) < 0.1
def test_measure_observables_zero_expectation(forest): """ Testing case when expectation value of observable should be close to zero """ qc = get_qc('2q-qvm') exptsetting = ExperimentSetting(plusZ(0), sX(0)) suite = TomographyExperiment([exptsetting], program=Program(I(0)), qubits=[0]) result = list( measure_observables(qc, suite, n_shots=10000, readout_symmetrize='exhaustive', calibrate_readout='plus-eig'))[0] np.testing.assert_almost_equal(result.expectation, 0.0, decimal=1)
def test_for_negative_probabilities(): # trivial program to do state tomography on prog = Program(I(0)) # make TomographyExperiment expt_settings = [ExperimentSetting(zeros_state([0]), pt) for pt in [sI(0), sX(0), sY(0), sZ(0)]] experiment_1q = TomographyExperiment(settings=expt_settings, program=prog) # make an abstract compiler class DummyCompiler(AbstractCompiler): def get_version_info(self): return {} def quil_to_native_quil(self, program: Program): return program def native_quil_to_executable(self, nq_program: Program): return nq_program # make a quantum computer object device = NxDevice(nx.complete_graph(1)) qc_density = QuantumComputer(name='testy!', qam=PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator), device=device, compiler=DummyCompiler()) # initialize with a pure state initial_density = np.array([[1.0, 0.0], [0.0, 0.0]]) qc_density.qam.wf_simulator.density = initial_density try: list(measure_observables(qc=qc_density, tomo_experiment=experiment_1q, n_shots=3000)) except ValueError as e: # the error is from np.random.choice by way of self.rs.choice in ReferenceDensitySimulator assert str(e) != 'probabilities are not non-negative' # initialize with a mixed state initial_density = np.array([[0.9, 0.0], [0.0, 0.1]]) qc_density.qam.wf_simulator.density = initial_density try: list(measure_observables(qc=qc_density, tomo_experiment=experiment_1q, n_shots=3000)) except ValueError as e: assert str(e) != 'probabilities are not non-negative'
def test_experiment_suite(): expts = [ ExperimentSetting(sI(), sX(0) * sY(1)), ExperimentSetting(sZ(0), sZ(0)), ] suite = TomographyExperiment(settings=expts, program=Program(X(0), Y(1)), qubits=[0, 1]) assert len(suite) == 2 for e1, e2 in zip(expts, suite): # experiment suite puts in groups of length 1 assert len(e2) == 1 e2 = e2[0] assert e1 == e2 prog_str = str(suite).splitlines()[0] assert prog_str == 'X 0; Y 1'
def test_measure_observables_symmetrize(forest): """ Symmetrization alone should not change the outcome on the QVM """ expts = [ ExperimentSetting(sI(), o1 * o2) for o1, o2 in itertools.product([sI(0), sX(0), sY(0), sZ(0)], [sI(1), sX(1), sY(1), sZ(1)]) ] suite = TomographyExperiment(expts, program=Program(X(0), CNOT(0, 1)), qubits=[0, 1]) assert len(suite) == 4 * 4 gsuite = group_experiments(suite) assert len( gsuite) == 3 * 3 # can get all the terms with I for free in this case qc = get_qc('2q-qvm') for res in measure_observables(qc, gsuite, n_shots=10_000, readout_symmetrize='exhaustive'):
def compile_tomo_expts(self): """ This method compiles the tomography experiment circuits and prepares them for simulation. Every time the circuits are adjusted, re-compiling the tomography experiments is required to affect the outcome. """ self.offset = 0 # use Forest's sorting algo from the Tomography suite to group Pauli measurements together experiments = [] for term in self.pauli_list: # if the Pauli term is an identity operator, add the term's coefficient directly to the VQE class' offset if len(term.operations_as_set()) == 0: self.offset += term.coefficient.real else: experiments.append(ExperimentSetting(TensorProductState(), term)) suite = TomographyExperiment(experiments, program=Program()) gsuite = group_experiments(suite) grouped_list = [] for setting in gsuite: group = [] for term in setting: group.append(term.out_operator) grouped_list.append(group) if self.verbose: print('Number of tomography experiments: ', len(grouped_list)) self.experiment_list = [] for group in grouped_list: self.experiment_list.append(GroupedPauliSetting(group, qc=self.qc, ref_state=self.ref_state, ansatz=self.ansatz, shotN=self.shotN, parametric_way=self.parametric_way, n_qubits=self.n_qubits, method=self.method, verbose=self.verbose, cq=self.custom_qubits))
def generate_exhaustive_state_dfe_experiment(program: Program, qubits: list, benchmarker: BenchmarkConnection) -> TomographyExperiment: """ Estimate state fidelity by exhaustive direct fidelity estimation. This leads to a quadratic reduction in overhead w.r.t. state tomography for fidelity estimation. The algorithm is due to: [DFE1] Practical Characterization of Quantum Devices without Tomography Silva et al., PRL 107, 210404 (2011) https://doi.org/10.1103/PhysRevLett.107.210404 https://arxiv.org/abs/1104.3835 [DFE2] Direct Fidelity Estimation from Few Pauli Measurements Flammia et al., PRL 106, 230501 (2011) https://doi.org/10.1103/PhysRevLett.106.230501 https://arxiv.org/abs/1104.4695 :param program: A program comprised of Clifford group gates that constructs a state for which we estimate the fidelity. :param qubits: The qubits to perform DFE on. This can be a superset of the qubits used in ``program``. :param benchmarker: A ``BecnhmarkConnection`` object to be used in experiment design :return: a set of experiments :rtype: ``TomographyExperiment` """ expr = TomographyExperiment(list( _exhaustive_dfe(program=program, qubits=qubits, in_states=[None, plusZ], benchmarker=benchmarker)), program=program, qubits=qubits) return expr
def expval(self, observable, wires, par): # Single-qubit observable if len(wires) == 1: # identify Experiment Settings for each of the possible single-qubit observables wire = wires[0] qubit = self.wiring[wire] d_expt_settings = { "Identity": [ExperimentSetting(TensorProductState(), sI(qubit))], "PauliX": [ExperimentSetting(TensorProductState(), sX(qubit))], "PauliY": [ExperimentSetting(TensorProductState(), sY(qubit))], "PauliZ": [ExperimentSetting(TensorProductState(), sZ(qubit))], "Hadamard": [ ExperimentSetting(TensorProductState(), float(np.sqrt(1 / 2)) * sX(qubit)), ExperimentSetting(TensorProductState(), float(np.sqrt(1 / 2)) * sZ(qubit)) ] } # expectation values for single-qubit observables if observable in [ "PauliX", "PauliY", "PauliZ", "Identity", "Hadamard" ]: prep_prog = Program() for instr in self.program.instructions: if isinstance(instr, Gate): # assumes single qubit gates gate, _ = instr.out().split(' ') prep_prog += Program(str(gate) + ' ' + str(qubit)) if self.readout_error is not None: prep_prog.define_noisy_readout(qubit, p00=self.readout_error[0], p11=self.readout_error[1]) tomo_expt = TomographyExperiment( settings=d_expt_settings[observable], program=prep_prog) grouped_tomo_expt = group_experiments(tomo_expt) meas_obs = list( measure_observables( self.qc, grouped_tomo_expt, active_reset=self.active_reset, symmetrize_readout=self.symmetrize_readout, calibrate_readout=self.calibrate_readout)) return np.sum( [expt_result.expectation for expt_result in meas_obs]) elif observable == 'Hermitian': # <H> = \sum_i w_i p_i Hkey = tuple(par[0].flatten().tolist()) w = self._eigs[Hkey]['eigval'] return w[0] * p0 + w[1] * p1 # Multi-qubit observable # ---------------------- # Currently, we only support qml.expval.Hermitian(A, wires), # where A is a 2^N x 2^N matrix acting on N wires. # # Eventually, we will also support tensor products of Pauli # matrices in the PennyLane UI. probs = self.probabilities(wires) if observable == 'Hermitian': Hkey = tuple(par[0].flatten().tolist()) w = self._eigs[Hkey]['eigval'] # <A> = \sum_i w_i p_i return w @ probs
def test_experiment_suite_empty(): suite = TomographyExperiment([], program=Program(X(0)), qubits=[0]) assert len(suite) == 0 assert str(suite.program) == 'X 0\n'