def run(self, qc: QuantumComputer,
            bitstring_map: Dict[str, str]) -> 'BernsteinVazirani':
        """
        Runs the Bernstein-Vazirani algorithm.

        Given a connection to a QVM or QPU, find the :math:`\\mathbf{a}` and :math:`b` corresponding
        to the function represented by the oracle function that will be constructed from the
        bitstring map.

        :param qc: connection to the QPU or QVM
        :param bitstring_map: a truth table describing the boolean function, whose dot-product
            vector and bias is to be found
        """
        # initialize all attributes
        self.input_bitmap = bitstring_map
        self.n_qubits = len(list(bitstring_map.keys())[0])
        self.computational_qubits = list(range(self.n_qubits))
        self.ancilla = self.n_qubits  # is the highest index now.

        # construct BV circuit
        self.bv_circuit = self._create_bv_circuit(bitstring_map)

        # find vector by running the full bv circuit
        full_circuit = Program()
        full_ro = full_circuit.declare('ro', 'BIT',
                                       len(self.computational_qubits) + 1)
        full_circuit += self.bv_circuit
        full_circuit += [
            MEASURE(qubit, ro)
            for qubit, ro in zip(self.computational_qubits, full_ro)
        ]
        full_executable = qc.compile(full_circuit)
        full_results = qc.run(full_executable)
        bv_vector = full_results[0][::-1]

        # To get the bias term we skip the Walsh-Hadamard transform
        ancilla_circuit = Program()
        ancilla_ro = ancilla_circuit.declare(
            'ro', 'BIT',
            len(self.computational_qubits) + 1)
        ancilla_circuit += self.bv_circuit
        ancilla_circuit += [MEASURE(self.ancilla, ancilla_ro[self.ancilla])]
        ancilla_executable = qc.compile(ancilla_circuit)
        ancilla_results = qc.run(ancilla_executable)
        bv_bias = ancilla_results[0][0]

        self.solution = ''.join([str(b) for b in bv_vector]), str(bv_bias)
        return self
示例#2
0
def get_parametric_program(qc: QuantumComputer,
                           q0: int,
                           q1: int,
                           n_shots: int = 1000):
    """
    Run one (beta, gamma) point for the simplest Maxcut QAOA.

    :param qc: The QuantumComputer to use
    :param beta: The beta angle (p = 1)
    :param gamma: The gamma angle (p = 1)
    :param q0: The index of the first qubit
    :param q1: The index of the second qubit
    :param n_shots: The number of shots to take for this (beta, gamma) point.
    """
    ising = generate_maxcut_ising(nx.from_edgelist([(0, 1)]))
    driver = sX(q0) + sX(q1)
    reward = sZ(q0) * sZ(q1) + 0  # add 0 to turn to PauliSum

    program = Program()
    beta = program.declare('beta', memory_type='REAL')
    gamma = program.declare('gamma', memory_type='REAL')
    program += get_qaoa_program(qubits=[q0, q1],
                                driver=driver,
                                reward=reward,
                                betas=[beta],
                                gammas=[gamma])
    ro = program.declare('ro', memory_size=2)
    program += MEASURE(q0, ro[0])
    program += MEASURE(q1, ro[1])
    program = program.wrap_in_numshots_loop(shots=n_shots)

    executable = qc.compile(program)
    return executable, ising
示例#3
0
    def find_bitstring(self, qc: QuantumComputer,
                       bitstring_map: Dict[str, int]) -> str:
        """
        Runs Grover's Algorithm to find the bitstring that is designated by ``bistring_map``.

        In particular, this will prepare an initial state in the uniform superposition over all bit-
        strings, an then use Grover's Algorithm to pick out the desired bitstring.

        :param qc: the connection to the Rigetti cloud to run pyQuil programs.
        :param bitstring_map: a mapping from bitstrings to the phases that the oracle should impart
            on them. If the oracle should "look" for a bitstring, it should have a ``-1``, otherwise
            it should have a ``1``.
        :return: Returns the bitstring resulting from measurement after Grover's Algorithm.
        """

        self._init_attr(bitstring_map)

        ro = self.grover_circuit.declare('ro', 'BIT', len(self.qubits))
        self.grover_circuit += [
            MEASURE(qubit, ro[idx]) for idx, qubit in enumerate(self.qubits)
        ]
        executable = qc.compile(self.grover_circuit)
        sampled_bitstring = qc.run(executable)

        return "".join([str(bit) for bit in sampled_bitstring[0]])
示例#4
0
def test_qpu_run():
    config = PyquilConfig()
    if config.qpu_url and config.qpu_compiler_url:
        g = nx.Graph()
        g.add_node(0)
        device = NxDevice(g)

        qc = QuantumComputer(
            name="pyQuil test QC",
            qam=QPU(endpoint=config.qpu_url, user="******"),
            device=device,
            compiler=QPUCompiler(
                quilc_endpoint=config.quilc_url,
                qpu_compiler_endpoint=config.qpu_compiler_url,
                device=device,
            ),
        )
        bitstrings = qc.run_and_measure(program=Program(X(0)), trials=1000)
        assert bitstrings[0].shape == (1000, )
        assert np.mean(bitstrings[0]) > 0.8
        bitstrings = qc.run(qc.compile(Program(X(0))))
        assert bitstrings.shape == (0, 0)
    else:
        pytest.skip(
            "QPU or compiler-server not available; skipping QPU run test.")
def with_quilc_parametric_compilation(
    *,
    quantum_computer: QuantumComputer,
    circuit: cirq.Circuit,
    resolvers: Sequence[cirq.ParamResolverOrSimilarType],
    repetitions: int,
    transformer: transformers.CircuitTransformer = transformers.default,
) -> List[cirq.Result]:
    """This `CircuitSweepExecutor` will compile the `circuit` using quilc as a
    parameterized `pyquil.api.QuantumExecutable` and on each iteration of
    `resolvers`, rather than resolving the `circuit` with `cirq.protocols.resolve_parameters`,
    it will attempt to cast the resolver to a dict and pass it as a memory map to
    to `pyquil.api.QuantumComputer`.

    Args:
        quantum_computer: The `pyquil.api.QuantumComputer` against which to execute the circuit.
        circuit: The `cirq.Circuit` to transform into a `pyquil.Program` and executed on the
            `quantum_computer`.
        resolvers: A sequence of parameter resolvers that this executor will write to memory
            on a copy of the `pyquil.api.QuantumExecutable` for each parameter sweep.
        repetitions: Number of times to run each iteration through the `resolvers`. For a given
            resolver, the `cirq.Result` will include a measurement for each repetition.
        transformer: A callable that transforms the `cirq.Circuit` into a `pyquil.Program`.
            You may pass your own callable or any function from `cirq_rigetti.circuit_transformers`.

    Returns:
        A list of `cirq.Result`, each corresponding to a resolver in `resolvers`.
    """

    program, measurement_id_map = transformer(circuit=circuit)
    program = _prepend_real_declarations(program=program, resolvers=resolvers)
    program.wrap_in_numshots_loop(repetitions)
    executable = quantum_computer.compile(program)

    cirq_results = []
    for resolver in resolvers:
        memory_map = _get_param_dict(resolver)
        logger.debug(
            f"running pre-compiled parametric circuit with parameters {memory_map}"
        )
        result = _execute_and_read_result(
            quantum_computer,
            executable.copy(),
            measurement_id_map,
            resolver,
            memory_map=memory_map,
        )
        cirq_results.append(result)

    return cirq_results
示例#6
0
def estimate_confusion_matrix(qc: QuantumComputer, qubit: int, num_shots: int = 10000) \
        -> np.ndarray:
    """
    Estimate the readout confusion matrix for a given qubit.

    The confusion matrix will be of the form::

        [[ p(0 | 0)     p(0 | 1) ]
         [ p(1 | 0)     p(1 | 1) ]]

    where each column sums to one. Note that the matrix need not be symmetric.

    :param qc: The quantum computer to estimate the confusion matrix.
    :param qubit: The actual physical qubit to measure
    :param num_shots: The number of shots to take. This function runs two programs, so
        the total number of shots taken will be twice this number.
    :return: a 2x2 confusion matrix for the qubit, where each row sums to one.
    """
    # prepare 0 (do nothing) and measure; repeat shots number of times
    zero_meas = Program()
    ro_zero = zero_meas.declare("ro", "BIT", 1)
    zero_meas += MEASURE(qubit, ro_zero[0])
    zero_meas.wrap_in_numshots_loop(num_shots)
    should_be_0 = qc.run(qc.compile(zero_meas))

    # prepare one and measure; repeat shots number of times
    one_meas = Program()
    one_meas += RX(pi, qubit)
    ro_one = one_meas.declare("ro", "BIT", 1)
    one_meas += MEASURE(qubit, ro_one[0])
    one_meas.wrap_in_numshots_loop(num_shots)
    should_be_1 = qc.run(qc.compile(one_meas))

    p00 = 1 - np.mean(should_be_0)
    p11 = np.mean(should_be_1)

    return np.array([[p00, 1 - p00], [1 - p11, p11]])
示例#7
0
def expectation_from_sampling(pyquil_program: Program,
                              marked_qubits: List[int], qc: QuantumComputer,
                              samples: int,
                              stacked_params: np.ndarray) -> float:
    """
    Calculation of Z_{i} at marked_qubits

    Given a wavefunctions, this calculates the expectation value of the Zi
    operator where i ranges over all the qubits given in marked_qubits.

    :param pyquil_program: pyQuil program generating some state
    :param marked_qubits: The qubits within the support of the Z pauli
                          operator whose expectation value is being calculated
    :param qc: A QuantumComputer object.
    :param samples: Number of bitstrings collected to calculate expectation
                    from sampling.
    :returns: The expectation value as a float.
    """
    program = Program()
    ro = program.declare('ro', 'BIT', max(marked_qubits) + 1)
    program += pyquil_program
    program += [
        MEASURE(qubit, r)
        for qubit, r in zip(list(range(max(marked_qubits) + 1)), ro)
    ]
    program.wrap_in_numshots_loop(samples)
    try:
        executable = qc.compile(program)
    except:
        import pdb
        pdb.set_trace()

    bitstring_samples = qc.run(executable)
    bitstring_tuples = list(map(tuple, bitstring_samples))

    freq = Counter(bitstring_tuples)

    # perform weighted average
    expectation = 0
    for bitstring, count in freq.items():
        bitstring_int = int("".join([str(x) for x in bitstring[::-1]]), 2)
        if parity_even_p(bitstring_int, marked_qubits):
            expectation += float(count) / samples
        else:
            expectation -= float(count) / samples
    return expectation
示例#8
0
def with_quilc_compilation_and_cirq_parameter_resolution(
    *,
    quantum_computer: QuantumComputer,
    circuit: cirq.Circuit,
    resolvers: Sequence[cirq.ParamResolverOrSimilarType],
    repetitions: int,
    transformer: transformers.CircuitTransformer = transformers.default,
) -> Sequence[cirq.Result]:
    """This `CircuitSweepExecutor` will first resolve each resolver in `resolvers` using
    `cirq.protocols.resolve_parameters` and then compile that resolved `cirq.Circuit` into
    native Quil using quilc. This executor may be useful if `with_quilc_parametric_compilation`
    fails to properly resolve a parameterized `cirq.Circuit`.

    Args:
        quantum_computer: The `pyquil.api.QuantumComputer` against which to execute the circuit.
        circuit: The `cirq.Circuit` to transform into a `pyquil.Program` and executed on the
            `quantum_computer`.
        resolvers: A sequence of parameter resolvers that `cirq.protocols.resolve_parameters` will
            use to fully resolve the circuit.
        repetitions: Number of times to run each iteration through the `resolvers`. For a given
            resolver, the `cirq.Result` will include a measurement for each repetition.
        transformer: A callable that transforms the `cirq.Circuit` into a `pyquil.Program`.
            You may pass your own callable or any function from `cirq_rigetti.circuit_transformers`.

    Returns:
        A list of `cirq.Result`, each corresponding to a resolver in `resolvers`.
    """

    cirq_results = []
    for resolver in resolvers:
        resolved_circuit = cirq.protocols.resolve_parameters(circuit, resolver)
        program, measurement_id_map = transformer(circuit=resolved_circuit)
        program.wrap_in_numshots_loop(repetitions)

        executable = quantum_computer.compile(program)
        result = _execute_and_read_result(
            quantum_computer, executable, measurement_id_map, resolver
        )
        cirq_results.append(result)

    return cirq_results
示例#9
0
def run_point(qc: QuantumComputer,
              beta: float,
              gamma: float,
              q0: int,
              q1: int,
              n_shots: int = 1000):
    """
    Run one (beta, gamma) point for the simplest Maxcut QAOA.

    :param qc: The QuantumComputer to use
    :param beta: The beta angle (p = 1)
    :param gamma: The gamma angle (p = 1)
    :param q0: The index of the first qubit
    :param q1: The index of the second qubit
    :param n_shots: The number of shots to take for this (beta, gamma) point.
    """

    # Construct a program
    ising = generate_maxcut_ising(nx.from_edgelist([(0, 1)]))
    driver = sX(q0) + sX(q1)
    reward = sZ(q0) * sZ(q1) + 0  # add 0 to turn to PauliSum

    program = get_qaoa_program(qubits=[q0, q1],
                               driver=driver,
                               reward=reward,
                               betas=[beta],
                               gammas=[gamma])
    ro = program.declare('ro', memory_size=2)
    program += MEASURE(q0, ro[0])
    program += MEASURE(q1, ro[1])
    program = program.wrap_in_numshots_loop(shots=n_shots)

    # Compile it
    executable = qc.compile(program)

    # Run and post-process
    bitstrings = qc.run(executable)
    rewards = calculate_ising_rewards(ising, bitstrings)
    return np.mean(rewards)
def test_set_initial_state(client_configuration: QCSClientConfiguration):
    # That is test the assigned state matrix in ReferenceDensitySimulator is persistent between
    # rounds of run.
    rho1 = np.array([[0.0, 0.0], [0.0, 1.0]])

    # run prog
    prog = Program(I(0))
    ro = prog.declare("ro", "BIT", 1)
    prog += MEASURE(0, ro[0])

    # make a quantum computer object
    device = NxQuantumProcessor(nx.complete_graph(1))
    qc_density = QuantumComputer(
        name="testy!",
        qam=PyQVM(n_qubits=1,
                  quantum_simulator_type=ReferenceDensitySimulator),
        compiler=DummyCompiler(quantum_processor=device,
                               client_configuration=client_configuration),
    )

    qc_density.qam.wf_simulator.set_initial_state(rho1).reset()

    out = [qc_density.run(prog).readout_data['ro'] for _ in range(0, 4)]
    ans = [np.array([[1]]), np.array([[1]]), np.array([[1]]), np.array([[1]])]
    assert all([np.allclose(x, y) for x, y in zip(out, ans)])

    # Run and measure style
    progRAM = prog.copy().wrap_in_numshots_loop(10)

    results = qc_density.run(qc_density.compile(progRAM))
    ans = {0: np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])}
    assert np.allclose(results.readout_data['ro'][0], ans[0])

    # test reverting ReferenceDensitySimulator to the default state
    rho0 = np.array([[1.0, 0.0], [0.0, 0.0]])
    qc_density.qam.wf_simulator.set_initial_state(rho0).reset()
    assert np.allclose(qc_density.qam.wf_simulator.density, rho0)
    assert np.allclose(qc_density.qam.wf_simulator.initial_density, rho0)
示例#11
0
    def is_constant(self, qc: QuantumComputer, bitstring_map: Dict[str, str]) -> bool:
        """Computes whether bitstring_map represents a constant function, given that it is constant
         or balanced. Constant means all inputs map to the same value, balanced means half of the
         inputs maps to one value, and half to the other.

        :param QVMConnection cxn: The connection object to the Rigetti cloud to run pyQuil programs.
        :param bitstring_map: A dictionary whose keys are bitstrings, and whose values are bits
         represented as strings.
        :type bistring_map: Dict[String, String]
        :return: True if the bitstring_map represented a constant function, false otherwise.
        :rtype: bool
        """
        self._init_attr(bitstring_map)

        prog = Program()
        dj_ro = prog.declare('ro', 'BIT', len(self.computational_qubits))
        prog += self.deutsch_jozsa_circuit
        prog += [MEASURE(qubit, ro) for qubit, ro in zip(self.computational_qubits, dj_ro)]
        executable = qc.compile(prog)
        returned_bitstring = qc.run(executable)
        # We are only running a single shot, so we are only interested in the first element.
        bitstring = np.array(returned_bitstring, dtype=int)
        constant = all([bit == 0 for bit in bitstring])
        return constant
示例#12
0
    def _sample_independent_bit_vectors(self, qc: QuantumComputer) -> None:
        """
        This method samples :math:`n-1` linearly independent vectors using the Simon Circuit.
        It attempts to put the sampled bitstring into a dictionary and only terminates once the
        dictionary contains :math:`n-1` samples

        :param qc: Connection object to a QuantumComputer.
        :return: None
        """
        while len(
                self._dict_of_linearly_indep_bit_vectors) < self.n_qubits - 1:

            prog = Program()
            simon_ro = prog.declare('ro', 'BIT',
                                    len(self.computational_qubits))
            prog += self.simon_circuit
            prog += [
                MEASURE(qubit, ro)
                for qubit, ro in zip(self.computational_qubits, simon_ro)
            ]
            executable = qc.compile(prog)
            sampled_bit_string = np.array(qc.run(executable)[0], dtype=int)

            self._add_to_dict_of_indep_bit_vectors(sampled_bit_string)
示例#13
0
def test_parametric_program(qc: QuantumComputer):
    compiled = qc.compile(
        Program(
            Declare("ro", "BIT", 1),
            Declare("theta", "REAL", 1),
            RX(MemoryReference("theta"), 0),
            MEASURE(0, ("ro", 0)),
        ).wrap_in_numshots_loop(1000),
    )

    all_results = []
    for theta in [0, np.pi, 2 * np.pi]:
        compiled.write_memory(region_name="theta", value=theta)
        results = qc.run(compiled).readout_data.get("ro")
        all_results.append(np.mean(results))

    if isinstance(qc.qam, QPU):
        assert all_results[0] < 0.2
        assert all_results[1] > 0.8
        assert all_results[2] < 0.2
    else:
        assert all_results[0] == 0.0
        assert all_results[1] > 0.8
        assert all_results[2] == 0.0
示例#14
0
 def run_program(
         program: Program,
         qc: QuantumComputer,
 ) -> np.ndarray:
     return qc.run(qc.compile(program)).readout_data.get('ro')
示例#15
0
def test_basic_program(qc: QuantumComputer):
    results = qc.run(qc.compile(TEST_PROGRAM)).readout_data.get("ro")

    assert results.shape == (1000, 2)
示例#16
0
    def __init__(
        self,
        list_gsuit_paulis: List[PauliTerm],
        qc: QuantumComputer,
        ref_state: Program,
        ansatz: Program,
        shotN: int,
        parametric_way: bool,
        n_qubits: int,
        active_reset: bool = True,
        cq=None,
        method='QC',
        verbose: bool = False,
        calibration: bool = True,
    ):
        """
        A tomography experiment class for use in VQE.
        In a real experiment, one only has access to measurements
        in the Z-basis, giving a result 0 or 1 for each qubit.
        The Hamiltonian may have terms like sigma_x or sigma_y,
        for which a basis rotation is required.
        As quantum mechanics prohibits measurements in multiple bases at once,
        the Hamiltonian needs to be grouped into commuting Pauli terms
        and for each set of terms the appropriate basis
        rotations is applied to each qubits.

        A parity_matrix is constructed to keep track of
        what consequence a qubit measurement of 0 or 1 has as a
        contribution to the Pauli estimation.

        The experiments are constructed as objects with class methods
        to run and adjust them.


        Instantiate using the following parameters:

        :param list() list_gsuit_paulis: list of Pauli terms which
                can be measured at the same time (they share a TPB!)
        :param QuantumComputer qc: QuantumComputer() object which
                will simulate the terms
        :param Program ref_state: Program() circuit object which
                produces the initial reference state (f.ex. Hartree-Fock)
        :param Program ansatz: Program() circuit object which
                produces the ansatz (f.ex. UCCSD)
        :param int shotN: number of shots to run this Setting for
        :param bool parametric_way: boolean whether to
                use parametric gates or hard-coded gates
        :param int n_qubits: total number of qubits used for the program
        :param bool active_reset: boolean whether or not
                to actively reset the qubits
        :param list() cq: list of qubit labels instead of
                the default [0,1,2,3,...,N-1]
        :param str method: string describing the computational
                method from {QC, linalg, WFS, Numpy}
        :param bool verbose: boolean, whether or not to give verbose
                output during execution

        """
        self.parametric_way = parametric_way
        self.pauli_list = list_gsuit_paulis
        self.shotN = shotN
        self.method = method
        self.parity_matrix = self.construct_parity_matrix(
            list_gsuit_paulis, n_qubits)
        self.verbose = verbose
        self.n_qubits = n_qubits
        self.cq = cq
        self.calibration = calibration

        if qc is not None:
            if qc.name[-4:] == 'yqvm' and self.cq is not None:
                raise NotImplementedError(
                    'manual qubit lattice feed'
                    ' for PyQVM not yet implemented. please set cq=None')
        else:
            if self.method == 'QC':
                raise ValueError('method is QC but no QuantumComputer'
                                 ' object supplied.')

        # instantiate a new program and construct it for compilation
        prog = Program()

        if self.method == 'QC':
            ro = prog.declare('ro',
                              memory_type='BIT',
                              memory_size=self.n_qubits)

        if active_reset and self.method == 'QC':
            if not qc.name[-4:] == 'yqvm':
                # in case of PyQVM, can not contain reset statement
                prog += RESET()

        # circuit which produces reference state (f.ex. Hartree-Fock)
        prog += ref_state

        # produce which prepares an ansatz state starting
        # from a reference state (f.ex. UCCSD or swap network UCCSD)
        prog += ansatz

        self.term_keys = []
        already_done = []
        for pauli in list_gsuit_paulis:
            # save the id for each term
            self.term_keys.append(pauli.operations_as_set())

            # also, we perform the necessary rotations
            # going from X or Y to Z basis
            for (i, st) in pauli.operations_as_set():
                if (st == 'X' or st == 'Y') and i not in already_done:
                    # note that 'i' is the *logical* index
                    # corresponding to the pauli.
                    if cq is not None:
                        # if the logical qubit should be remapped
                        # to physical qubits, access this cq
                        prog += pauli_meas(cq[i], st)
                    else:
                        prog += pauli_meas(i, st)
                    # if we already have rotated the basis
                    # due to another term, don't do it again!
                    already_done.append(i)

        if self.method != 'QC':
            self.pure_pyquil_program = Program(prog)
        else:
            # measure the qubits and assign the result
            # to classical register ro
            for i in range(self.n_qubits):
                if cq is not None:
                    prog += MEASURE(cq[i], ro[i])
                else:
                    prog += MEASURE(i, ro[i])

            prog2 = percolate_declares(prog)

            # wrap in shotN number of executions on the qc,
            # to get operator measurement by sampling
            prog2.wrap_in_numshots_loop(shots=self.shotN)

            # print(self.term_keys)
            # print circuits:
            # from pyquil.latex import to_latex
            # print(to_latex(prog2))
            # input()

            if qc.name[-4:] == 'yqvm':
                self.pyqvm_program = prog2
            else:
                self.pyquil_executable = qc.compile(prog2)

            # print("compiled")
            # print(to_latex(qc.compiler.quil_to_native_quil(prog2)))
            # input()

        # now about calibration
        if self.calibration and self.method != 'QC':
            # turn off calibration if not QC.
            self.calibration = False
        if self.calibration:
            # prepare and run the calibration experiments
            prog = Program()
            ro = prog.declare('ro',
                              memory_type='BIT',
                              memory_size=self.n_qubits)

            if active_reset:
                if not qc.name[-4:] == 'yqvm':
                    # in case of PyQVM, can not contain reset statement
                    prog += RESET()

            # circuit which produces reference state,
            # which is in the case of calibration experiments
            # the same as the out_operators.
            already_done = []
            for pauli in list_gsuit_paulis:
                # also, we perform the necessary rotations
                # going to X/Y/Z =1 state
                for (i, st) in pauli.operations_as_set():
                    if (st == 'X' or st == 'Y') and i not in already_done:
                        # note that 'i' is the *logical* index
                        # corresponding to the pauli.
                        if cq is not None:
                            # if the logical qubit should be remapped
                            # to physical qubits, access this cq
                            prog += pauli_meas(cq[i], st).dagger()
                        else:
                            prog += pauli_meas(i, st).dagger()
                        # if we already have rotated the basis
                        # due to another term, don't do it again!
                        already_done.append(i)
            # measurement now
            already_done = []
            for pauli in list_gsuit_paulis:
                # also, we perform the necessary rotations
                # going from X or Y to Z basis
                for (i, st) in pauli.operations_as_set():
                    if (st == 'X' or st == 'Y') and i not in already_done:
                        # note that 'i' is the *logical* index
                        # corresponding to the pauli.
                        if cq is not None:
                            # if the logical qubit should be remapped
                            # to physical qubits, access this cq
                            prog += pauli_meas(cq[i], st)
                        else:
                            prog += pauli_meas(i, st)
                        # if we already have rotated the basis
                        # due to another term, don't do it again!
                        already_done.append(i)

            # measure the qubits and assign the result
            # to classical register ro
            for i in range(self.n_qubits):
                if cq is not None:
                    prog += MEASURE(cq[i], ro[i])
                else:
                    prog += MEASURE(i, ro[i])

            prog2 = percolate_declares(prog)
            # wrap in shotN number of executions on the qc,
            # to get operator measurement by sampling
            prog2.wrap_in_numshots_loop(shots=self.shotN)

            if qc.name[-4:] == 'yqvm':
                self.pyqvm_program = prog2
                bitstrings = qc.run(self.pyqvm_program)
            # compile to native quil if it's not a PYQVM
            else:
                pyquil_executable = qc.compile(prog2)
                bitstrings = qc.run(pyquil_executable)
            # start data processing
            # this matrix computes the pauli string parity,
            # and stores that for each bitstring
            is_odd = np.mod(bitstrings.dot(self.parity_matrix), 2)
            # if the parity is odd, the bitstring gives a -1 eigenvalue,
            # and +1 vice versa.
            # sum over all bitstrings, average over shotN shots,
            # and weigh each pauli string by its coefficient
            e_array = 1 - 2 * np.sum(is_odd, axis=0) / self.shotN
            self.calibration_norms = e_array
            print(f"calibration_norms: {e_array}")