示例#1
0
def generate_parametric_cz_phase_ramsey_program(qcid: int,
                                                other_qcid: int) -> Program:
    """
    Generate a single CZ phase Ramsey experiment at a given phase.

    :param qcid: The qubit to move around the Bloch sphere and measure the incurred RZ on.
    :param other_qcid: The other qubit that constitutes a two-qubit pair along with `qcid`.
    :param phase: The phase kick to supply after playing the CZ pulse on the equator.
    :param num_shots: The number of shots to average over for the data point.
    :return: A parametric Program for performing a CZ Ramsey experiment.
    """
    program = Program()
    # NOTE: only need readout register for `qcid` not `other_qcid` since `other_qcid` is only
    #       needed to identify which CZ gate we're using
    ro = program.declare('ro', 'BIT', 1)
    theta = program.declare('theta', 'REAL')

    # go to the equator
    program += Program(RX(np.pi / 2, qcid))
    # apply the CZ gate - note that CZ is symmetric, so the order of qubits doesn't matter
    program += Program(CZ(qcid, other_qcid))
    # go to |1> after a phase kick
    program += Program(RZ(theta, qcid), RX(np.pi / 2, qcid))

    program += MEASURE(qcid, ro[0])

    return program
def measure_graph_state(graph: nx.Graph, focal_node: int):
    """Given a graph state, measure a focal node and its neighbors with a particular measurement
    angle.

    :param prep_program: Probably the result of :py:func:`create_graph_state`.
    :param qs: List of qubits used in prep_program or anything that can be indexed by the nodes
        in the graph ``graph``.
    :param graph: The graph state graph. This is needed to figure out what the neighbors are
    :param focal_node: The node in the graph to serve as the focus. The focal node is measured
        at an angle and all its neighbors are measured in the Z basis
    :return Program, list of classical offsets into the ``ro`` register.
    """
    program = Program()
    theta = program.declare('theta', 'REAL')
    program += RY(theta, focal_node)

    neighbors = sorted(graph[focal_node])
    ro = program.declare('ro', 'BIT', len(neighbors) + 1)

    program += MEASURE(focal_node, ro[0])
    for i, neighbor in enumerate(neighbors):
        program += MEASURE(neighbor, ro[i + 1])

    classical_addresses = list(range(len(neighbors) + 1))
    return program, classical_addresses
示例#3
0
def parameterized_euler_rotations(
    qubits: Sequence[int],
    *,
    prefix: str,
    suffix_alpha: str = "alpha",
    suffix_beta: str = "beta",
    suffix_gamma: str = "gamma",
) -> Program:
    """
    Given a number of qubits (n), build a ``Program`` containing a ZXZXZ-decomposed gate on each
    qubit, where each ``RZ`` is parameterized by declared values with labels given by the "prefix"
    and "suffix" arguments. Put more plainly, the resulting Quil program on n qubits is::

        RZ(alpha_label[0]) 0
        RX(pi/2) 0
        RZ(beta_label[0]) 0
        RX(-pi/2) 0
        RZ(gamma_label[0]) 0
        ...
        RZ(alpha_label[n-1]) n-1
        RX(pi/2) n-1
        RZ(beta_label[0]) n-1
        RX(-pi/2) n-1
        RZ(gamma_label[n-1]) n-1

    :param qubits: The number of qubits (n).
    :param prefix: The prefix for the declared memory region labels. For example, if the prefix
        is "preparation" and the alpha, beta, and gamma suffixes are left as default, the labels
        would be "preparation_alpha", "preparation_beta", and "preparation_gamma".
    :param suffix_alpha: The suffix for the "alpha" memory region label, which corresponds to the
        first (rightmost) ``Z`` in the ZXZXZ decomposition. Defaults to "alpha".
    :param suffix_beta: The suffix for the "beta" memory region label, which corresponds to the
        second (middle) ``Z`` in the ZXZXZ decomposition. Defaults to "beta".
    :param suffix_gamma: The suffix for the "gamma" memory region label, which corresponds to the
        last (leftmost) ``Z`` in the ZXZXZ decomposition. Defaults to "gamma".
    :return: A ``Program`` containing a 3 parameterized ``RZ``s and 2 fixed ``RX``s per qubit.
    """
    alpha_label = f"{prefix}_{suffix_alpha}"
    beta_label = f"{prefix}_{suffix_beta}"
    gamma_label = f"{prefix}_{suffix_gamma}"

    p = Program()

    alpha = p.declare(alpha_label, "REAL", len(qubits))
    beta = p.declare(beta_label, "REAL", len(qubits))
    gamma = p.declare(gamma_label, "REAL", len(qubits))

    for idx, q in enumerate(qubits):
        p += RZ(alpha[idx], q)
        p += RX(np.pi / 2, q)
        p += RZ(beta[idx], q)
        p += RX(-np.pi / 2, q)
        p += RZ(gamma[idx], q)

    return p
示例#4
0
def test_subtracting_memory_regions():
    # https://github.com/rigetti/pyquil/issues/709
    p = Program()
    alpha = p.declare("alpha", "REAL")
    beta = p.declare("beta", "REAL")
    p += RZ(alpha - beta, 0)
    p2 = Program(p.out())
    parsed_rz = p2.pop()  # type: Gate
    parsed_param = parsed_rz.params[0]
    assert isinstance(parsed_param, Sub)
    assert parsed_param.op1 == alpha
    assert parsed_param.op2 == beta
示例#5
0
    def calibration_circuits(
            self,
            allow_multi_qubit: Optional[bool] = True) -> Dict[int, Program]:
        """Create a list of Program objects for calibrating.
        
        Args:
            allow_multi_qubit: A bool indicating whether we want to calibrate both qubits simultaneously.
            
        Returns:
            A list of Program objects for the calibration experiments.
        """
        circuits = dict()

        # in case we want to calibrate both qubits simultaneously
        if allow_multi_qubit:
            # start with calibration of |0>
            prog = Program('PRAGMA INITIAL_REWIRING "NAIVE"')
            ro = prog.declare('ro', 'BIT', 2)

            for index, qubit in enumerate(sorted(self._qubits)):
                prog += Program().measure(qubit, ro[index])
            circuits['0'] = prog

            # continue with |1>
            prog = Program('PRAGMA INITIAL_REWIRING "NAIVE"')
            ro = prog.declare('ro', 'BIT', 2)

            for index, qubit in enumerate(sorted(self._qubits)):
                prog += X(qubit)
                prog += Program().measure(qubit, ro[index])

            circuits['1'] = prog

        # otherwise
        else:

            # start with |0> state for both qubits separately
            for index, qubit in enumerate(self._qubits):
                prog = Program('PRAGMA INITIAL_REWIRING "NAIVE"')
                ro = prog.declare('ro', 'BIT', 1)
                prog += Program().measure(qubit, ro[0])
                circuits[str(qubit) + '0'] = prog
            # continue with |1>, again separately for the two qubits
            for index, qubit in enumerate(self._qubits):
                prog = Program('PRAGMA INITIAL_REWIRING "NAIVE"')
                ro = prog.declare('ro', 'BIT', 1)
                prog += X(qubit)
                prog += Program().measure(qubit, ro[0])
                circuits[str(qubit) + '1'] = prog

        return circuits
示例#6
0
    def build_circuit(self, qubit_ops):
        program = Program()
        self.qubits = [program.alloc() for _ in range(len(qubit_ops))]

        program_readout_reg = program.declare("ro",
                                              memory_type="BIT",
                                              memory_size=len(self.qubits))

        for qb, gatestr in zip(self.qubits, qubit_ops):
            if gatestr == "I":
                pass
            elif gatestr == "X":
                program.gate("X", qubits=[qb], params=[])
            elif gatestr == "H":
                program.gate("H", qubits=[qb], params=[])
            elif gatestr == "K":  # our one char CX label
                program.gate(
                    "CNOT",
                    qubits=[self.qubits[self.qubits.index(qb) - 1], qb],
                    params=[])

        program.measure_all(*zip(self.qubits, program_readout_reg))
        program = address_qubits(program)
        program.wrap_in_numshots_loop(shots=10)

        self.program = program
示例#7
0
    def test_X_fidelity(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 1)
        raw_prog += gates.X(0)
        raw_prog += gates.X(0)
        raw_prog += gates.X(0)
        raw_prog += gates.MEASURE(0, ro[0])

        ft_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        is_correct = lambda result: result[0] == 1

        trials = 100000
        correct, elapsed = self.run_and_benchmark_program(raw_prog,
                                                          trials,
                                                          is_correct,
                                                          separate=False)
        print(correct, trials, elapsed)

        trials = 20
        correct, elapsed = self.run_and_benchmark_program(ft_prog,
                                                          trials,
                                                          is_correct,
                                                          separate=True)
        print(correct, trials, elapsed)
示例#8
0
    def run_and_measure(self, program: Program, trials: int):
        """
        Run the provided state preparation program and measure all qubits.

        .. note::

            In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates
            noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``.
            For faster noise-free simulation, consider
            :py:class:`WavefunctionSimulator.run_and_measure`.

        :param program: The state preparation program to run and then measure.
        :param trials: The number of times to run the program.
        :return: A numpy array of shape (trials, n_device_qubits) that contains 0s and 1s
        """
        program = program.copy()
        for instr in program.instructions:
            if not isinstance(instr, Gate) and not isinstance(instr, Reset):
                raise ValueError(
                    "run_and_measure programs must consist only of quantum gates."
                )
        ro = program.declare('ro', 'BIT',
                             max(self.device.qubit_topology().nodes) + 1)
        for q in sorted(self.device.qubit_topology().nodes):
            program.inst(MEASURE(q, ro[q]))
        program.wrap_in_numshots_loop(trials)
        executable = self.compile(program)
        return self.run(executable=executable)
示例#9
0
def new_logical_qubit(prog: Program, qecc: QECC, name: str) -> CodeBlock:
    n = qecc.n
    raw_mem = prog.declare(name, 'BIT', 2 * n)
    mem = MemoryChunk(raw_mem, 0, raw_mem.declared_size)
    qubits = [QubitPlaceholder() for _ in range(n)]
    _initialize_memory(prog, raw_mem, qubits)
    return CodeBlock(qubits, mem[:n], mem[n:])
示例#10
0
def generate_single_depth_experiment(rotation: Program,
                                     depth: int,
                                     exp_type: str,
                                     axis: Tuple = None) -> Program:
    """
    Generate an experiment for a single depth where the type specifies a final measurement of either
    X or Y. The rotation program is repeated depth number of times, and we assume the rotation is
    about the axis (theta, phi).

    :param rotation: the program specifying the gate whose angle of rotation we wish to estimate.
    :param depth: the number of times we apply the rotation in the experiment
    :param exp_type: X or Y, specifying which operator to measure at the end of the experiment
    :param axis: the axis of rotation. If none is specified, axis is assumed to be the Z axis. (rotation should be RZ)
    :return: a program specifying the entire experiment of a single iteration of the RPE protocol in [RPE]
    """
    experiment = Program()
    ro_bit = experiment.declare("ro", "BIT", 1)
    qubit = list(rotation.get_qubits())[0]
    prepare_state(experiment, qubit, axis)
    for _ in range(depth):
        experiment.inst(rotation)
    if axis:
        experiment.inst(RZ(-axis[1], qubit))
        experiment.inst(RY(-axis[0], qubit))
    local_pauli_eig_meas(experiment, exp_type, qubit)
    experiment.measure(qubit, ro_bit)
    return experiment
示例#11
0
def generate_single_t1_experiment(qubits: Union[int, List[int]],
                                  time: float,
                                  n_shots: int = 1000) -> Program:
    """
    Return a t1 program in native Quil for a single time point.

    :param qubits: Which qubits to measure.
    :param time: The decay time before measurement.
    :param n_shots: The number of shots to average over for the data point.
    :return: A T1 Program.
    """
    program = Program()

    try:
        len(qubits)
    except TypeError:
        qubits = [qubits]

    ro = program.declare('ro', 'BIT', len(qubits))
    for q in qubits:
        program += RX(np.pi, q)
        program += Pragma('DELAY', [q], str(time))
    for i in range(len(qubits)):
        program += MEASURE(qubits[i], ro[i])
    program.wrap_in_numshots_loop(n_shots)
    return program
示例#12
0
def generate_single_rabi_experiment(qubits: Union[int, List[int]],
                                    theta: float,
                                    n_shots: int = 1000) -> Program:
    """
    Return a Rabi program in native Quil rotated through the given angle.

    Rabi oscillations are observed by applying successively larger rotations to the same initial
    state.

    :param qubits: Which qubits to measure.
    :param theta: The angle of the Rabi RX rotation.
    :param n_shots: The number of shots to average over for the data point.
    :return: A Program that rotates through a given angle about the X axis.
    """
    program = Program()

    try:
        len(qubits)
    except TypeError:
        qubits = [qubits]

    ro = program.declare('ro', 'BIT', len(qubits))
    for q in qubits:
        program += RX(theta, q)
    for i in range(len(qubits)):
        program += MEASURE(qubits[i], ro[i])
    program.wrap_in_numshots_loop(n_shots)
    return program
示例#13
0
def period_helper(a, N, size):
    c1 = QubitPlaceholder()
    zero = QubitPlaceholder()
    x = QubitPlaceholder.register(size)
    b = QubitPlaceholder.register(size + 1)
    #takes in x and b as zero, finds
    p = Program()

    n = 2 * size
    def_regs = Program()
    period_regs = def_regs.declare('ro', 'BIT', n)

    #For one reg, we want H, CUA, R_i m_i, X^m_i
    for i in range(n - 1, -1, -1):
        R = Program()
        R += H(c1)
        for j in range(i - 1, -1, -1):
            k = i - j + 1
            doit = Program()
            doit += RK(k)(c1).dagger()
            R = Program().if_then(period_regs[j], doit, I(c1)) + R
        R += MEASURE(c1, period_regs[i])
        R += Program().if_then(period_regs[i], X(c1), I(c1))
        #R = Program(H(c1)) + R
        R = Program(H(c1)) + UA(c1, x, b, a**(2**i), N, zero) + R
        p = R + p
    p = write_in(1, x) + p
    p = def_regs + p
    p = get_defs() + p
    p = address_qubits(p)
    return p
示例#14
0
    def superdense_coding_program(self, bit0: int, bit1: int):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 2)
        # Prepare Bell pair
        raw_prog += gates.H(0)
        raw_prog += gates.CNOT(0, 1)
        # Alice controls qubit 0 and Bob controls 1
        if bit0 == 0 and bit1 == 0:
            pass
        if bit0 == 0 and bit1 == 1:
            raw_prog += gates.X(0)
        if bit0 == 1 and bit1 == 0:
            raw_prog += gates.Z(0)
        if bit0 == 1 and bit1 == 1:
            raw_prog += gates.X(0)
            raw_prog += gates.Z(0)
        # Now Alice sends qubit 0 to Bob
        # Bob rotates from Bell basis to standard basis
        raw_prog += gates.CNOT(0, 1)
        raw_prog += gates.H(0)
        # Measure qubits into Bob's registers
        raw_prog += gates.MEASURE(0, ro[0])
        raw_prog += gates.MEASURE(1, ro[1])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[0], bit0)
            self.assertEqual(result[1], bit1)
示例#15
0
def parameterized_bitstring_prep(qubits: Sequence[int],
                                 reg_name: str,
                                 append_measure: bool = False,
                                 in_x_basis: bool = False) -> Program:
    """
    Produces a parameterized program for the given group of qubits, where each qubit is prepared
    in the 0 or 1 state depending on the parameterization specified at run-time.

    See also bitstring_prep which produces a non-parameterized program that prepares
    and measures a single pre-specified bitstring on the given qubits. Parameterization allows
    for a single program to measure each bitstring (specified at run-time) and speeds up
    the collective measurements of all bitstring for a group of qubits. Note that the program
    produced by bitstring_prep does not execute any gates when preparing 0 on a particular qubit
    and executes only one gate to prepare 1; meanwhile, this method produces a program which
    executes three gates for either preparation on each qubit.

    :param qubits: labels of qubits on which some bitstring will be prepared and, perhaps, measured
    :param reg_name: the name of the register that will hold the bitstring parameters.
    :param append_measure: dictates whether to measure the qubits after preparing the bitstring
    :param in_x_basis: if true, prepare the bitstring in the x basis, where plus <==> zero,
        minus <==> one.
    :return: a parameterized program capable of preparing, and optionally measuring, any runtime
        specified bitstring on the given qubits. See estimate_joint_confusion_in_set in
        readout.py for example use.
    """
    program = Program()

    if append_measure:
        ro = program.declare('ro', memory_type='BIT', memory_size=len(qubits))

    bitstr_reg = program.declare(reg_name,
                                 memory_type='REAL',
                                 memory_size=len(qubits))
    for idx, qubit in enumerate(qubits):
        program += RX(pi / 2, qubit)
        program += RZ(pi * bitstr_reg[idx], qubit)
        program += RX(-pi / 2, qubit)

        # if we are doing logic in X basis, follow each bit preparation with a Hadamard
        # H |0> = |+> and H |1> = |-> where + and - label the X basis vectors.
        if in_x_basis:
            program += H(qubit)

        if append_measure:
            program += MEASURE(qubit, ro[idx])

    return program
示例#16
0
def _generate_random_program(n_qubits, length, include_measures=False):
    """Randomly sample gates and arguments (qubits, angles)"""
    if n_qubits < 3:
        raise ValueError(
            "Please request n_qubits >= 3 so we can use 3-qubit gates.")

    gates = list(QUANTUM_GATES.values())

    prog = Program()
    if include_measures:
        gates.append(MEASURE)
        # one bit of classical memory per qubit
        prog.declare("ro", "BIT", n_qubits)
    for _ in range(length):
        gate = random.choice(gates)
        possible_qubits = set(range(n_qubits))
        sig = inspect.signature(gate)

        param_vals = []
        for param in sig.parameters:
            if param in [
                    "qubit",
                    "q1",
                    "q2",
                    "control",
                    "control1",
                    "control2",
                    "target",
                    "target_1",
                    "target_2",
            ]:
                param_val = random.choice(list(possible_qubits))
                possible_qubits.remove(param_val)
            elif param == "classical_reg":
                qubit = random.choice(list(possible_qubits))
                param_val = MemoryReference("ro", qubit)
                possible_qubits.remove(qubit)
            elif param == "angle":
                param_val = random.uniform(-2 * pi, 2 * pi)
            else:
                raise ValueError("Unknown gate parameter {}".format(param))

            param_vals.append(param_val)

        prog += gate(*param_vals)

    return prog
示例#17
0
        def _ansatz():
            """Mimics the circuits from the appendix of the paper, with the exception of
            the ordering of the CNOT gates (before and after compilation they still do not match).
            This is probably best just left up to the compiler. 

            For ease of reading the printed operations, the qubits are looped over several times.
            """
            program = Program()
            qubits = self.qc.qubits()
            n_qubits = self.q
            var = 'theta'

            ro = program.declare('ro', memory_type='BIT', memory_size=n_qubits)
            thetas = {
                var + str(qubit): program.declare(var + str(qubit),
                                                  memory_type='REAL')
                for qubit in qubits
            }

            sq = int(np.sqrt(n_qubits))
            lim = n_qubits - sq - 1

            for m in qubits:
                program += RX(thetas[var + str(m)], m)

            for m in qubits:
                m_1 = m + 1
                m_sq = m + sq
                skip = (m_1) % sq

                if m_1 < n_qubits:
                    if (m_sq >= n_qubits): program += CNOT(m, m_1)
                    else: program += CNOT(m, m_sq)

                if (m < lim) and (skip != 0): program += CNOT(m, m_1)

            for m in qubits:
                program += MEASURE(m, ro[m])

            print("program instructions from pyquil:\n")
            for instruction in program.instructions:
                print(instruction)

            return program
示例#18
0
def uccsd_ansatz_circuit_parametric(n_orbitals, n_electrons, cq=None):
    """
    This function returns a UCCSD variational ansatz with hard-coded gate angles. The number of orbitals specifies the
    number of qubits, the number of electrons specifies the initial HF reference state which is assumed was prepared.
    The list cq is an optional list which specifies the qubit label ordering which is to be assumed.


    :param int n_orbitals: number of spatial orbitals in the molecule (for building UCCSD singlet generator)
    :param int n_electrons: number of electrons in the molecule
    :param list() cq: custom qubits
    
    :return: program which prepares the UCCSD :math:`T_1 + T_2` propagator with a spin-singlet assumption.
    :rtype: Program
    """
    # determine number of required parameters
    trotter_steps = 1
    m = (2 * n_orbitals - n_electrons) * n_electrons / 4
    n_parameters = int(trotter_steps * (m * (m + 3)) / 2)

    prog = Program()
    memref = prog.declare('theta',
                          memory_type='REAL',
                          memory_size=n_parameters)

    fermionic_list, indices = uccsd_singlet_generator_with_indices(
        2 * n_orbitals, n_electrons)

    pauli_list = []
    index_dict = {}
    for i, fermionic_term in enumerate(fermionic_list):
        pauli_sum = qubitop_to_pyquilpauli(
            jordan_wigner(normal_ordered(fermionic_term)))
        for term in pauli_sum:
            new_term = term
            # if the QPU has a custom lattice labeling, supplied by user through a list cq, reorder the Pauli labels.
            if cq is not None:
                new_term = term.coefficient
                for pauli in term:
                    new_index = cq[pauli[0]]
                    op = pauli[1]
                    new_term = new_term * PauliTerm(op=op, index=new_index)

            pauli_list.append(new_term)
            # keep track of the indices in a dictionary
            index_dict[new_term.operations_as_set()] = indices[i]

    # add each term as successive exponentials (1 trotter step, and not taking into account commutation relations!)
    term_sets = commuting_sets(simplify_pauli_sum(PauliSum(pauli_list)))

    for j, terms in enumerate(term_sets):
        prog += exponentiate_commuting_pauli_sum_parametric(
            terms, index_dict, memref)

    return prog
示例#19
0
    def test_single_Z_program(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 1)
        raw_prog += gates.Y(0)
        raw_prog += gates.MEASURE(0, ro[0])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[0], 1)
def append_measure_register(program: Program,
                            qubits: List = None,
                            trials: int = 10,
                            ham: PauliSum = None) -> Program:
    """Creates readout register, MEASURE instructions for register and wraps
    in trials trials.

    Parameters
    ----------
    param qubits : list
        List of Qubits to measure. If None, program.get_qubits() is used
    param trials : int
        The number of trials to run.
    param ham : PauliSum
        Hamiltonian to whose basis we need to switch. All terms in it must
        trivially commute!

    Returns
    -------
    Program :
        program with the gate change and measure instructions appended
    """
    base_change_gates = {'X': lambda qubit: H(qubit),
                         'Y': lambda qubit: RX(np.pi / 2, qubit),
                         'Z': lambda qubit: I(qubit)}

    if qubits is None:
        qubits = program.get_qubits()


    def _get_correct_gate(qubit: Union[int, QubitPlaceholder]) -> Program():
        """Correct base change gate on the qubit `qubit` given `ham`"""
        # this is an extra function, because `return` allows us to
        # easily break out of loops
        for term in ham:
            if term[qubit] != 'I':
                return base_change_gates[term[qubit]](qubit)

        raise ValueError(f"PauliSum {ham} doesn't act on qubit {qubit}")

    # append to correct base change gates if ham is specified. Otherwise
    # assume diagonal basis
    if ham is not None:
        for qubit in ham.get_qubits():
            program += Program(_get_correct_gate(qubit))

    # create a read out register
    ro = program.declare('ro', memory_type='BIT', memory_size=len(qubits))

    # add measure instructions to the specified qubits
    for i, qubit in enumerate(qubits):
        program += MEASURE(qubit, ro[i])
    program.wrap_in_numshots_loop(trials)
    return program
示例#21
0
def test_if_then_2():
    # if FALSE creg, then measure 0 should give 1
    prog = Program()
    creg = prog.declare("creg", "BIT")
    prog.inst(MOVE(creg, 0), X(0))
    branch_a = Program(X(0))
    branch_b = Program()
    prog.if_then(creg, branch_a, branch_b)
    prog += MEASURE(0, creg)
    qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator)
    qam.execute(prog)
    assert qam.ram["creg"][0] == 1
示例#22
0
    def run_and_measure(self, program: Program,
                        trials: int) -> Dict[int, np.ndarray]:
        """
        Run the provided state preparation program and measure all qubits.

        The returned data is a dictionary keyed by qubit index because qubits for a given
        QuantumComputer may be non-contiguous and non-zero-indexed. To turn this dictionary
        into a 2d numpy array of bitstrings, consider::

            bitstrings = qc.run_and_measure(...)
            bitstring_array = np.vstack([bitstrings[q] for q in qc.qubits()]).T
            bitstring_array.shape  # (trials, len(qc.qubits()))

        .. note::

            If the target :py:class:`QuantumComputer` is a noiseless :py:class:`QVM` then
            only the qubits explicitly used in the program will be measured. Otherwise all
            qubits will be measured. In some circumstances this can exhaust the memory
            available to the simulator, and this may be manifested by the QVM failing to
            respond or timeout.

        .. note::

            In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates
            noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``.
            For faster noise-free simulation, consider
            :py:class:`WavefunctionSimulator.run_and_measure`.

        :param program: The state preparation program to run and then measure.
        :param trials: The number of times to run the program.
        :return: A dictionary keyed by qubit index where the corresponding value is a 1D array of
            measured bits.
        """
        program = program.copy()
        validate_supported_quil(program)
        ro = program.declare("ro", "BIT", len(self.qubits()))
        measure_used = isinstance(self.qam,
                                  QVM) and self.qam.noise_model is None
        qubits_to_measure = set(
            map(qubit_index, program.get_qubits()) if measure_used else self.
            qubits())
        for i, q in enumerate(qubits_to_measure):
            program.inst(MEASURE(q, ro[i]))
        program.wrap_in_numshots_loop(trials)
        executable = self.compile(program)
        bitstring_array = self.run(executable=executable)
        bitstring_dict = {}
        for i, q in enumerate(qubits_to_measure):
            bitstring_dict[q] = bitstring_array[:, i]
        for q in set(self.qubits()) - set(qubits_to_measure):
            bitstring_dict[q] = np.zeros(trials)
        return bitstring_dict
def test_if_then():
    # if TRUE creg, then measure 0 should give 0
    prog = Program()
    creg = prog.declare('creg', 'BIT')
    prog.inst(MOVE(creg, 1), X(0))
    branch_a = Program(X(0))
    branch_b = Program()
    prog.if_then(creg, branch_a, branch_b)
    prog += MEASURE(0, creg)
    qam = PyQVM(n_qubits=1,
                quantum_simulator_type=ReferenceWavefunctionSimulator)
    qam.execute(prog)
    assert qam.ram['creg'][0] == 0
示例#24
0
def measure_qubits(qubits: Sequence[int]) -> Program:
    """
    Given a number of qubits (n), produce a ``Program`` with a ``MEASURE`` instruction on qubits
    0 through n-1, with corresponding readout registers ro[0] through ro[n-1].

    :param qubits: The number of qubits (n).
    :return: A ``Program`` that measures n qubits.
    """
    p = Program()
    ro = p.declare("ro", "BIT", len(qubits))
    for idx, q in enumerate(qubits):
        p += MEASURE(q, ro[idx])
    return p
示例#25
0
def test_while():
    init_register = Program()
    classical_flag_register = init_register.declare("classical_flag_register", "BIT")
    init_register += MOVE(classical_flag_register, True)

    loop_body = Program(X(0), H(0)).measure(0, classical_flag_register)

    # Put it all together in a loop program:
    loop_prog = init_register.while_do(classical_flag_register, loop_body)

    qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator)
    qam.execute(loop_prog)
    assert qam.ram[classical_flag_register.name][0] == 0
示例#26
0
    def test_multiple_measurements_program(self):
        raw_prog = Program()
        ro = raw_prog.declare('ro', 'BIT', 2)
        raw_prog += gates.H(0)
        raw_prog += gates.MEASURE(0, ro[0])
        raw_prog.if_then(ro[0], gates.X(0), Program())
        raw_prog += gates.MEASURE(0, ro[1])

        new_prog = ftqc.rewrite_program(raw_prog, self.steane_7bit)

        results = self.run_program(new_prog)
        for result in results:
            self.assertEqual(result[1], 0)
示例#27
0
def _naive_program_generator(qc: QuantumComputer, qubits: Sequence[int],
                             permutations: np.ndarray,
                             gates: np.ndarray) -> Program:
    """
    Naively generates a native quil program to implement the circuit which is comprised of the given
    permutations and gates.

    :param qc: the quantum resource that will implement the PyQuil program for each model circuit
    :param qubits: the qubits available for the implementation of the circuit. This naive
        implementation simply takes the first depth-many available qubits.
    :param permutations: array of depth-many arrays of size n_qubits indicating a qubit permutation
    :param gates: a depth by depth//2 array of matrices representing the 2q gates at each layer.
        The first row of matrices is the earliest-time layer of 2q gates applied.
    :return: a PyQuil program in native_quil instructions that implements the circuit represented by
        the input permutations and gates. Note that the qubits are measured in the proper order
        such that the results may be directly compared to the simulated heavy hitters from
        collect_heavy_outputs.
    """
    num_measure_qubits = len(permutations[0])
    # at present, naively select the minimum number of qubits to run on
    qubits = qubits[:num_measure_qubits]

    # create a simple program that uses the compiler to directly generate 2q gates from the matrices
    prog = Program()
    for layer_idx, (perm, layer) in enumerate(zip(permutations, gates)):
        for gate_idx, gate in enumerate(layer):
            # get the Quil definition for the new gate
            g_definition = DefGate(
                "LYR" + str(layer_idx) + "_RAND" + str(gate_idx), gate)
            # get the gate constructor
            G = g_definition.get_constructor()
            # add definition to program
            prog += g_definition
            # add gate to program, acting on properly permuted qubits
            prog += G(int(qubits[perm[gate_idx]]),
                      int(qubits[perm[gate_idx + 1]]))

    ro = prog.declare("ro", "BIT", len(qubits))
    for idx, qubit in enumerate(qubits):
        prog.measure(qubit, ro[idx])

    native_quil = qc.compiler.quil_to_native_quil(prog)

    if not set(native_quil.get_qubits()).issubset(set(qubits)):
        raise ValueError(
            "naive_program_generator could not generate program using only the "
            "qubits supplied. Please provide your own program_generator if you wish "
            "to use only the qubits specified.")

    return native_quil
示例#28
0
def parameterized_readout_symmetrization(qubits: Sequence[int], label: str = "symmetrization") -> Program:
    """
    Given a number of qubits (n), produce a ``Program`` with an ``RX`` instruction on qubits
    0 through n-1, parameterized by memory regions label[0] through label[n-1], where "label"
    defaults to "symmetrization".

    :param qubits: The number of qubits (n).
    :param label: The name of the declared memory region.
    :return: A ``Program`` with parameterized ``RX`` gates on n qubits.
    """
    p = Program()
    symmetrization = p.declare(f"{label}", "REAL", len(qubits))
    for idx, q in enumerate(qubits):
        p += RX(symmetrization[idx], q)
    return p
示例#29
0
def test_measure_all():
    p = Program()
    mem = p.declare("ro", memory_size=4)
    p.measure_all((0, mem[0]), (1, mem[1]), (2, mem[3]))
    assert p.out(
    ) == "DECLARE ro BIT[4]\nMEASURE 0 ro[0]\nMEASURE 1 ro[1]\nMEASURE 2 ro[3]\n"

    p = Program([H(idx) for idx in range(4)])
    p.measure_all()
    for idx in range(4):
        assert p[idx + 5] == MEASURE(idx, MemoryReference("ro", idx))

    p = Program()
    p.measure_all()
    assert p.out() == ""
示例#30
0
def read_out(p, reg):

    p = Program(p)
    ro = p.declare('ro', 'BIT', len(reg))

    for i in range(len(reg)):
        p += MEASURE(reg[i], ro[i])

    p = address_qubits(p)
    result = qvm.run(p)

    outp = 0
    for i in range(len(result[0])):
        if (result[0][i] == 1):
            outp += 2**i
    return (outp, p)
示例#31
0
    def __init__(self, qc, file_prefix, num_bits, hamil,
                all_var_nums, fun_name_to_fun,
                do_resets=True, **kwargs):
        """
        Constructor

        Do in constructor as much hamil indep stuff as possible so don't
        have to redo it with every call to cost fun. Also,
        when self.num_samples !=0,  we store a dict called term_to_exec
        mapping an executable (output of Rigetti compile() function) to a
        term,  for each term in the hamiltonian hamil. When num_samples=0,
        term_to_exec={}

        Parameters
        ----------
        qc : QuantumComputer
        file_prefix : str
        num_bits : int
        hamil : QubitOperator
        all_var_nums : list[int]
        fun_name_to_fun : dict[str, function]
        do_resets : bool
        kwargs : dict
            key-words args of MeanHamilMinimizer constructor

        Returns
        -------

        """

        MeanHamil.__init__(self, file_prefix, num_bits, hamil,
                           all_var_nums, fun_name_to_fun, **kwargs)
        self.qc = qc
        self.do_resets = do_resets

        # this creates a file with all PyQuil gates that
        # are independent of hamil. Gates may contain free parameters
        self.translator = Qubiter_to_RigettiPyQuil(
            self.file_prefix, self.num_bits,
            aqasm_name='RigPyQuil', prelude_str='', ending_str='')
        with open(self.translator.aqasm_path, 'r') as fi:
            self.translation_line_list = fi.readlines()

        pg = Program()
        self.pg = pg
        if self.num_samples:

            # pg prelude
            pg += Pragma('INITIAL_REWIRING', ['"PARTIAL"'])
            if self.do_resets:
                pg += RESET()
            ro = pg.declare('ro', 'BIT', self.num_bits)
            s = ''
            for var_num in self.all_var_nums:
                vname = self.translator.vprefix + str(var_num)
                s += vname
                s += ' = pg.declare("'
                s += vname
                s += '", memory_type="REAL")\n'
            exec(s)

            # add to pg the operations that are independent of hamil
            for line in self.translation_line_list:
                line = line.strip('\n')
                if line:
                    exec(line)

            len_pg_in = len(pg)

            # hamil loop to store executables for each term in hamil
            self.term_to_exec = {}
            for term, coef in self.hamil.terms.items():

                # reset pg to initial length.
                # Temporary work-around to bug
                # in PyQuil ver 2.5.0.
                # Slicing was changing
                # pg from type Program to type list
                pg = Program(pg[:len_pg_in])
                self.pg = pg

                # add xy measurements coda to pg
                bit_pos_to_xy_str =\
                    {bit: action for bit, action in term if action != 'Z'}
                RigettiTools.add_xy_meas_coda_to_program(
                    pg, bit_pos_to_xy_str)

                # request measurements
                for i in range(self.num_bits):
                    pg += MEASURE(i, ro[i])

                pg.wrap_in_numshots_loop(shots=self.num_samples)

                executable = self.qc.compile(pg)
                # print(",,,...", executable)
                self.term_to_exec[term] = executable