Exemple #1
0
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)
Exemple #2
0
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
Exemple #3
0
    def compile_tomo_expts(self, pauli_list=None):
        """
        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 = []
        if pauli_list is None:
            pauli_list = self.pauli_list
        for term in 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:
                # initial state and term pair.
                experiments.append(ExperimentSetting(
                        TensorProductState(),
                        term))

        suite = Experiment(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,
                            ))
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
def tomographize(qc, program: Program, qubits: List[int], num_shots=1000, t_type='lasso', pauli_num=None):
    """
    Runs tomography on the state generated by program and estimates the state. Can be used as a debugger

    :param qc: the quantum computer to run the debugger on
    :param program: which program to run the tomography on
    :param quibits: whihc qubits to run the tomography debugger on
    :param num_shots: the number of times to run each tomography experiment to get the expected value
    :param t_type: which tomography type to use. Possible values: "linear_inv", "mle", "compressed_sensing", "lasso"
    :param pauli_num: the number of pauli matrices to use in the tomography
    :return: the density matrix as an ndarray
    """

    # if no pauli_num is specified use the maximum
    if pauli_num==None:
        pauli_num=len(Qubits)

    #Generate experiments
    qubit_experiments = generate_state_tomography_experiment(program=program, qubits=qubits)
    
    exp_list = []
    #Experiment holds all 2^n possible pauli matrices for the given number of qubits
    #Now take pauli_num random pauli matrices as per the paper's advice
    if (pauli_num > len(qubit_experiments)):
        print("Cannot sample more Pauli matrices thatn d^2!")
        return None
    exp_list = random.sample(list(qubit_experiments), pauli_num)
    input_exp = PyQuilTomographyExperiment(settings=exp_list, program=program)

    #Group experiments if possible to minimize QPU runs
    input_exp = group_experiments(input_exp)

    #NOTE: Change qvm depending on whether we are simulating qvm
    qc.compiler.quil_to_native_quil = basic_compile

    results = list(measure_observables(qc=qc, tomo_experiment=input_exp, n_shots=num_shots))
    
    if t_type == 'compressed_sensing':
        return compressed_sensing_state_estimate(results=results, qubits=qubits)
    elif t_type == 'mle':
        return iterative_mle_state_estimate(results=results, qubits=qubits)      
    elif t_type == "linear_inv":
        return linear_inv_state_estimate(results=results, qubits=qubits)
    elif t_type == "lasso":
        return lasso_state_estimate(results=results, qubits=qubits)
Exemple #7
0
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'):
Exemple #8
0
    def compile_tomo_expts(self, pauli_list=None):
        """
        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.
        """
        # use Forest's sorting algo from the Tomography suite
        # to group Pauli measurements together
        settings = []
        if pauli_list is None:
            pauli_list = self.pauli_list

        for term in pauli_list:
            # skip an identity operator,
            if len(term.operations_as_set()) > 0:
                # initial state and term pair.
                settings.append(ExperimentSetting(
                        TensorProductState(),
                        term))
        # group_experiments cannot be directly run
        # because there may be experiment with multiple settings.
        # so here we just use group_experiments to sort out the settings,
        # then reset the experiments with additional measurements.
        experiments = Experiment(settings, Program())
        suite = group_experiments(experiments)
        # we just need the grouped settings.
        grouped_pauil_terms = []
        for setting in suite:
            group = []
            for i, term in enumerate(setting):
                pauil_term = term.out_operator.copy()
                # Coefficients to be multiplied.
                pauil_term.coefficient = complex(1.)
                if i == 0:
                    group.append(pauil_term)
                elif len(pauil_term) > len(group[0]):
                    group.insert(0, pauil_term)
                else:
                    group.append(pauil_term)
            # make sure the longest pauil_term contains all the small
            # pauil_terms. Otherwise, we prepare a bigger one.
            bigger_pauli = group[0]
            if len(group) > 1:
                for term in group[1:]:
                    for iq, op in term.operations_as_set():
                        if op != group[0][iq]:
                            assert(group[0][iq] == "I"), \
                                    (f"{term} and {group[0]}"
                                    " not compatible!")
                            if bigger_pauli is group[0]:
                                bigger_pauli == group[0].copy()
                            bigger_pauli *= PauliTerm(op, iq)
                if bigger_pauli is not group[0]:
                    print(f"new pauli_term generated: {bigger_pauli}")
                    group.insert(0, bigger_pauli)
            grouped_pauil_terms.append(group)
        # group settings with additional_expectations.
        grouped_settings = []
        for pauil_terms in grouped_pauil_terms:
            additional = None
            if len(pauil_terms) > 1:
                additional = []
                for term in pauil_terms[1:]:
                    additional.append(term.get_qubits())
            grouped_settings.append(
                    ExperimentSetting(TensorProductState(),
                            pauil_terms[0],
                            additional_expectations=additional))

        # get the uccsd program
        prog = Program()
        prog += RESET()
        prog += self.ref_state + self.ansatz
        prog.wrap_in_numshots_loop(shots=self.shotN)
        self.experiment_list = Experiment(grouped_settings, prog)
        print('Number of tomography experiments: ',
                len(self.experiment_list))
        # calibration expreimental results.
        self.calibrations = self.qc.calibrate(self.experiment_list)
Exemple #9
0
    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
Exemple #10
0
    def expval(self, observable):
        wires = observable.wires
        # 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.name in [
                    "PauliX", "PauliY", "PauliZ", "Identity", "Hadamard"
            ]:
                prep_prog = Program()
                for instr in self.program.instructions:
                    if isinstance(instr, Gate):
                        # split gate and wires -- assumes 1q and 2q gates
                        tup_gate_wires = instr.out().split(' ')
                        gate = tup_gate_wires[0]
                        str_instr = str(gate)
                        # map wires to qubits
                        for w in tup_gate_wires[1:]:
                            str_instr += f' {int(w)}'
                        prep_prog += Program(str_instr)

                if self.readout_error is not None:
                    prep_prog.define_noisy_readout(qubit,
                                                   p00=self.readout_error[0],
                                                   p11=self.readout_error[1])

                # All observables are rotated and can be measured in the PauliZ basis
                tomo_expt = Experiment(settings=d_expt_settings["PauliZ"],
                                       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.name == '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

        return super().expval(observable)
Exemple #11
0
    def expval(self, observable):
        wires = observable.wires

        # `measure_observables` called only when parametric compilation is turned off
        if not self.parametric_compilation:

            # Single-qubit observable
            if len(wires) == 1:

                # Ensure sensible observable
                assert observable.name in [
                    "PauliX", "PauliY", "PauliZ", "Identity", "Hadamard"
                ], "Unknown observable"

                # Create appropriate PauliZ operator
                wire = wires[0]
                qubit = self.wiring[wire]
                pauli_obs = sZ(qubit)

            # Multi-qubit observable
            elif len(wires) > 1 and isinstance(
                    observable, Tensor) and not self.parametric_compilation:

                # All observables are rotated to be measured in the Z-basis, so we just need to
                # check which wires exist in the observable, map them to physical qubits, and measure
                # the product of PauliZ operators on those qubits
                pauli_obs = sI()
                for wire in observable.wires:
                    qubit = wire
                    pauli_obs *= sZ(self.wiring[qubit])

            # Program preparing the state in which to measure observable
            prep_prog = Program()
            for instr in self.program.instructions:
                if isinstance(instr, Gate):
                    # split gate and wires -- assumes 1q and 2q gates
                    tup_gate_wires = instr.out().split(" ")
                    gate = tup_gate_wires[0]
                    str_instr = str(gate)
                    # map wires to qubits
                    for w in tup_gate_wires[1:]:
                        str_instr += f" {int(w)}"
                    prep_prog += Program(str_instr)

            if self.readout_error is not None:
                for wire in observable.wires:
                    qubit = wire
                    prep_prog.define_noisy_readout(self.wiring[qubit],
                                                   p00=self.readout_error[0],
                                                   p11=self.readout_error[1])

            # Measure out multi-qubit observable
            tomo_expt = Experiment(
                settings=[ExperimentSetting(TensorProductState(), pauli_obs)],
                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 the estimated expectation value
            return np.sum(
                [expt_result.expectation for expt_result in meas_obs])

        # Calculation of expectation value without using `measure_observables`
        return super().expval(observable)