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 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"
Beispiel #2
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)
Beispiel #3
0
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))
Beispiel #4
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 test_variance_bootstrap():
    qubits = [0, 1]
    qc = get_test_qc(n_qubits=len(qubits))
    state_prep = Program([H(q) for q in qubits])
    state_prep.inst(CZ(qubits[0], qubits[1]))
    tomo_expt = generate_state_tomography_experiment(state_prep, qubits)
    results = list(
        measure_observables(qc=qc, tomo_experiment=tomo_expt, n_shots=10_000))
    estimate, status = iterative_mle_state_estimate(results=results,
                                                    qubits=qubits,
                                                    dilution=0.5)
    rho_est = estimate.estimate.state_point_est
    purity = np.trace(rho_est @ rho_est)
    purity = np.real_if_close(purity)
    assert purity.imag == 0.0

    def my_mle_estimator(_r, _q):
        return iterative_mle_state_estimate(results=_r,
                                            qubits=_q,
                                            dilution=0.5,
                                            entropy_penalty=0.0,
                                            beta=0.0)[0]

    boot_purity, boot_var = estimate_variance(results=results,
                                              qubits=qubits,
                                              tomo_estimator=my_mle_estimator,
                                              functional=dm.purity,
                                              n_resamples=5,
                                              project_to_physical=False)

    np.testing.assert_allclose(purity,
                               boot_purity,
                               atol=2 * np.sqrt(boot_var),
                               rtol=0.01)
def measurement_func(request, test_qc):
    if request.param == 'wfn':
        return lambda expt: list(wfn_measure_observables(n_qubits=2, tomo_expt=expt))
    elif request.param == 'sampling':
        return lambda expt: list(measure_observables(qc=test_qc,
                                                     tomo_experiment=expt, n_shots=4000))
    else:
        raise ValueError()
 def run(qc: QuantumComputer, seq: List[Program], subgraph: List[List[int]], num_trials: int) -> np.ndarray:
     prog = merge_programs(seq)
     # TODO: parallelize
     results = []
     for qubits in subgraph:
         state_prep = prog
         tomo_exp = generate_state_tomography_experiment(state_prep, qubits=qubits)
         _rs = list(measure_observables(qc, tomo_exp, num_trials))
         # Inelegant shim from state tomo refactor. To clean up!
         expectations=[r.expectation for r in _rs[1:]]
         variances=[r.std_err ** 2 for r in _rs[1:]]
         results.append((expectations, variances))
     return results
Beispiel #8
0
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'))
Beispiel #9
0
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
Beispiel #10
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
Beispiel #11
0
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)
Beispiel #12
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)
Beispiel #13
0
def single_q_tomo_fixture():
    qubits = [0]
    qc = get_test_qc(n_qubits=len(qubits))

    # Generate random unitary
    u_rand = haar_rand_unitary(2 ** 1, rs=np.random.RandomState(52))
    state_prep = Program().defgate("RandUnitary", u_rand)
    state_prep.inst([("RandUnitary", qubits[0])])

    # True state
    wfn = NumpyWavefunctionSimulator(n_qubits=1)
    psi = wfn.do_gate_matrix(u_rand, qubits=[0]).wf.reshape(-1)
    rho_true = np.outer(psi, psi.T.conj())

    # Get data from QVM
    tomo_expt = generate_state_tomography_experiment(state_prep, qubits)
    results = list(measure_observables(qc=qc, tomo_experiment=tomo_expt, n_shots=4000))

    return results, rho_true
Beispiel #14
0
    def do_tomography(self, arg: str) -> None:
        """tom(ography) [qubit_index [qubit_index...]]
        Runs state tomography on the qubits specified by the space-separated
        list of qubit indices. Without argument, run on all qubits in Program
        so far (but first ask confirmation).
        """
        qubits = []
        if not arg:
            try:
                reply = input("Run on all qubits? ")
            except EOFError:
                reply = "no"
            reply = reply.strip().lower()
            if reply in ("y", "yes"):
                qubits = list(self.program.get_qubits())
        else:
            try:
                qubits = [int(x) for x in arg.split()]
            except ValueError:
                self.message(
                    "Qubit indices must be specified as a space-separated list"
                )
                return

        trimmed_program = trim_program(self.program, qubits)
        if not QuilControlFlowGraph(trimmed_program).is_dag():
            raise ValueError("Program is not a dag!")

        experiment = generate_state_tomography_experiment(
            trimmed_program, qubits)
        # TODO: Let user specify n_shots (+ other params)
        results = list(
            measure_observables(qc=self.qc,
                                tomo_experiment=experiment,
                                n_shots=1000))
        # TODO: Let user specify algorithm
        rho_est = linear_inv_state_estimate(results, qubits)
        self.message(np.round(rho_est, 4))
        self.message("Purity: {}".format(np.trace(np.matmul(rho_est,
                                                            rho_est))))
        self.recreate_wavefunction(rho_est)
Beispiel #15
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'):
Beispiel #16
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
                     mitigate_readout_errors=True) -> DFEData:
    """
    Acquire data necessary for direct fidelity estimate (DFE).

    :param qc: A quantum computer object where the experiment will run.
    :param expr: A partial tomography(``TomographyExperiment``) object describing the experiments to be run.
    :param n_shots: The minimum number of shots to be taken in each experiment (including calibration).
    :param active_reset: Boolean flag indicating whether experiments should terminate with an active reset instruction
        (this can make experiments a lot faster).
    :param mitigate_readout_errors: Boolean flag indicating whether bias due to imperfect readout should be corrected
        for
    :return: a DFE data object
    :rtype: ``DFEData`
    """
    if mitigate_readout_errors:
        res = list(measure_observables(qc, expr, n_shots=n_shots, active_reset=active_reset))
    else:
        res = list(measure_observables(qc, expr, n_shots=n_shots, active_reset=active_reset, readout_symmetrize=None,
                                       calibrate_readout=None))

    return DFEData(results=res,
                   in_states=[str(e[0].in_state) for e in expr],
                   program=expr.program,
                   out_pauli=[str(e[0].out_operator) for e in expr],
                   pauli_point_est=np.array([r.expectation for r in res]),
                   pauli_std_err=np.array([r.stddev for r in res]),
                   cal_point_est=np.array([r.calibration_expectation for r in res]),
                   cal_std_err=np.array([r.calibration_stddev for r in res]),
                   dimension=2**len(expr.qubits),
                   qubits=expr.qubits)
Beispiel #18
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)
Beispiel #19
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)