def test_pyquil_program():
    """Tests if the Dynamic Decoupling Sequence gives rise to Identity
    operation in PyQuil
    """
    _duration = 5e-6
    _offsets = [0, 1e-6, 2.5e-6, 4e-6, 5e-6]
    _rabi_rotations = [np.pi / 2, np.pi / 2, np.pi, 0, np.pi / 2]
    _azimuthal_angles = [0, 0, np.pi / 2, 0, 0]
    _detuning_rotations = [0, 0, 0, np.pi, 0]

    sequence = DynamicDecouplingSequence(
        duration=_duration,
        offsets=_offsets,
        rabi_rotations=_rabi_rotations,
        azimuthal_angles=_azimuthal_angles,
        detuning_rotations=_detuning_rotations)

    program = convert_dds_to_pyquil_program(sequence, [0], gate_time=1e-6)

    assert len(program) == 13
    assert program[0] == Pragma("PRESERVE_BLOCK")
    assert program[-1] == Pragma("END_PRESERVE_BLOCK")
    assert program[1] == RX(np.pi / 2, 0)
    assert program[2] == I(0)
    assert program[3] == RX(np.pi / 2, 0)
    assert program[4] == I(0)
    assert program[5] == RY(np.pi, 0)
    assert program[6] == I(0)
    assert program[7] == RZ(np.pi, 0)
    assert program[8] == I(0)
    assert program[9] == RX(np.pi / 2, 0)
Esempio n. 2
0
def test_warn_on_pragma_with_trailing_measures():
    "Check that to_latex warns when measurement alignment conflicts with gate group pragma."
    with pytest.warns(UserWarning):
        _ = to_latex(Program(Declare('ro', 'BIT'),
                             Pragma("LATEX_GATE_GROUP"),
                             MEASURE(0, MemoryReference('ro')),
                             Pragma("END_LATEX_GATE_GROUP"),
                             MEASURE(1, MemoryReference('ro'))))
Esempio n. 3
0
def test_gate_group_pragma():
    "Check that to_latex does not fail on LATEX_GATE_GROUP pragma."
    p = Program()
    p.inst(Pragma("LATEX_GATE_GROUP", [], 'foo'),
           X(0),
           X(0),
           Pragma("END_LATEX_GATE_GROUP"),
           X(1))
    _ = to_latex(p)
Esempio n. 4
0
def change_of_basis_matrix_to_quil(qc: QuantumComputer, qubits: Sequence[int],
                                   change_of_basis: np.ndarray) -> Program:
    """
    Helper to return a native quil program for the given qc to implement the change_of_basis matrix.

    :param qc: Quantum Computer that will need to use the change of basis
    :param qubits: the qubits the program should act on
    :param change_of_basis: a unitary matrix acting on len(qubits) many qubits
    :return: a native quil program that implements change_of_basis on the qubits of qc.
    """
    prog = Program()
    # ensure the program is compiled onto the proper qubits
    prog += Pragma('INITIAL_REWIRING', ['"NAIVE"'])
    g_definition = DefGate("COB", change_of_basis)
    # get the gate constructor
    COB = g_definition.get_constructor()
    # add definition to program
    prog += g_definition
    # add gate to program
    prog += COB(*qubits)
    # compile to native quil
    nquil = qc.compiler.quil_to_native_quil(prog)

    # strip the program to only what we need, i.e. the gates themselves.
    only_gates = Program([inst for inst in nquil if isinstance(inst, Gate)])

    return only_gates
Esempio n. 5
0
    def _run_program(self, program):
        program = program.copy()

        if self.qpu:
            # time to go through the compiler. whee!
            pragma = Program(
                [Pragma("INITIAL_REWIRING", ['"PARTIAL"']),
                 RESET()])
            program = pragma + program
            program = self._wrap_program(program)
            nq_program = self._qc.compiler.quil_to_native_quil(program)
            gate_count = sum(1 for instr in nq_program
                             if isinstance(instr, Gate))
            executable = self._qc.compiler.native_quil_to_executable(
                nq_program)
            results = self._qc.run(executable=executable)
        else:
            program = self._wrap_program(program)
            gate_count = len(program)
            results = self._qc.run(program)

        info = {
            "gate_count": gate_count
        }  # compiled length for qpu, uncompiled for qvm
        return results, info
Esempio n. 6
0
def test_validate_protoquil_with_pragma():
    prog = Program(
        RESET(),
        H(1),
        Pragma('DELAY'),
        MEASURE(1)
    )
    assert prog.is_protoquil()
Esempio n. 7
0
def test_validate_supported_quil_with_pragma():
    prog = Program(
        RESET(),
        H(1),
        Pragma('DELAY'),
        MEASURE(1, None)
    )
    assert prog.is_supported_on_qpu()
Esempio n. 8
0
def test_noisy_instruction():
    # Unaffected
    assert I(0) == _noisy_instruction(I(0))
    assert RZ(.234)(0) == _noisy_instruction(RZ(.234)(0))
    assert Pragma('lalala') == _noisy_instruction(Pragma('lalala'))

    # Noisified
    assert _noisy_instruction(CZ(0, 1)).out() == 'noisy-cz 0 1'
    assert _noisy_instruction(RX(np.pi / 2)(0)).out() == 'noisy-x-plus90 0'
    assert _noisy_instruction(RX(-np.pi / 2)(23)).out() == 'noisy-x-minus90 23'

    # Unsupported
    with pytest.raises(ValueError):
        _noisy_instruction(H(0))

    with pytest.raises(ValueError):
        _noisy_instruction(RX(2 * np.pi / 3)(0))
Esempio n. 9
0
    def pre_expval(self):
        """Run the QVM"""
        # pylint: disable=attribute-defined-outside-init
        for e in self.expval_queue:
            wire = [e.wires[0]]

            if e.name == 'PauliX':
                # X = H.Z.H
                self.apply('Hadamard', wire, [])

            elif e.name == 'PauliY':
                # Y = (HS^)^.Z.(HS^) and S^=SZ
                self.apply('PauliZ', wire, [])
                self.apply('S', wire, [])
                self.apply('Hadamard', wire, [])

            elif e.name == 'Hadamard':
                # H = Ry(-pi/4)^.Z.Ry(-pi/4)
                self.apply('RY', wire, [-np.pi/4])

            elif e.name == 'Hermitian':
                # For arbitrary Hermitian matrix H, let U be the unitary matrix
                # that diagonalises it, and w_i be the eigenvalues.
                H = e.parameters[0]
                Hkey = tuple(H.flatten().tolist())

                if Hkey in self._eigs:
                    # retrieve eigenvectors
                    U = self._eigs[Hkey]['eigvec']
                else:
                    # store the eigenvalues corresponding to H
                    # in a dictionary, so that they do not need to
                    # be calculated later
                    w, U = np.linalg.eigh(H)
                    self._eigs[Hkey] = {'eigval': w, 'eigvec': U}

                # Perform a change of basis before measuring by applying U^ to the circuit
                self.apply('QubitUnitary', wire, [U.conj().T])

        prag = Program(Pragma('INITIAL_REWIRING', ['"PARTIAL"']))

        if self.active_reset:
            prag += RESET()

        self.prog = prag + self.prog

        qubits = list(self.prog.get_qubits())
        ro = self.prog.declare('ro', 'BIT', len(qubits))
        for i, q in enumerate(qubits):
            self.prog.inst(MEASURE(q, ro[i]))

        self.prog.wrap_in_numshots_loop(self.shots)
        executable = self.qc.compile(self.prog)

        bitstring_array = self.qc.run(executable=executable)
        self.state = {}
        for i, q in enumerate(qubits):
            self.state[q] = bitstring_array[:, i]
Esempio n. 10
0
def test_validate_supported_quil_multiple_measures():
    prog = Program(
        RESET(),
        H(1),
        Pragma('DELAY'),
        MEASURE(1, None),
        MEASURE(1, None)
    )
    with pytest.raises(ValueError):
        validate_supported_quil(prog)
def hello_qmi(device_name: str = "9q-generic-qvm", shots: int = 5) -> None:
    """
    Get acquainted with your quantum computer by asking it to perform a simple
    coin-toss experiment. Involve 3 qubits in this experiment, and ask each one
    to give `shots` many results.

    :param device_name: The name of a quantum computer which can be retrieved
                        from `pyquil.api.get_qc()`. To find a list of all
                        devices, you can use `pyquil.api.list_devices()`.
    """
    # Initialize your Quil program
    program = Program()
    # Allow the compiler to re-index to use available qubits, if necessary.
    program += Pragma('INITIAL_REWIRING', ['"GREEDY"'])
    device = query_device(device_name)
    if device is not None:
        # device_name refers to a real (QPU) device, so let's construct
        # the program from the device's qubits.
        readout = program.declare('ro', 'BIT', len(device['qubits']))
        for qubit in device['qubits'].values():
            program += RX(math.pi / 2, qubit)
        for idx, qubit in enumerate(device['qubits'].values()):
            program += MEASURE(qubit, readout[idx])
    else:
        # device_name refers to a non-real (QVM) device, so let's construct
        # the program from arbitrary qubits, e.g. 0, 1, and 2

        # Declare 3 bits of memory space for the readout results of all three qubits
        readout = program.declare('ro', 'BIT', 3)
        # For each qubit, apply a pulse to move the qubit's state halfway between
        # the 0 state and the 1 state
        program += RX(math.pi / 2, 0)
        program += RX(math.pi / 2, 1)
        program += RX(math.pi / 2, 2)
        # Add measurement instructions to measure the qubits and record the result
        # into the respective bit in the readout register
        program += MEASURE(0, readout[0])
        program += MEASURE(1, readout[1])
        program += MEASURE(2, readout[2])

    # This tells the program how many times to run the above sequence
    program.wrap_in_numshots_loop(shots)

    # Get the quantum computer we want to run our experiment on
    qc = get_qc(device_name)

    # Compile the program, specific to which quantum computer we are using
    compiled_program = qc.compile(program)

    # Run the program and get the shots x 3 array of results
    results = qc.run(compiled_program)
    # Print the results. We expect to see (shots x 3) random 0's and 1's
    print(
        f"Your{' virtual' if isinstance(qc.qam, QVM) else ''} quantum "
        f"computer, {device_name}, greets you with:\n", results)
Esempio n. 12
0
def exponentiate_reward(pauli_sum, param):
    """
    Calculates the exponential representation of the given QAOA problem hamiltonian term. This is
    separated out from the driver as it allows for manual "compilation" by setting the edges and
    their order to reflect the topology of any hardware the code is supposed to run on.

    :param pauli_sum: PauliSum of the problem hamiltonian
    :param param: parameter set (angles) for the problem hamiltonian
    :param edge_ordering: List of pairs of connected qubits in the desired order, or None for
        the default order.
    :return: pyQuil.Program for the exponential
    """
    prog = Program()
    prog += Pragma('COMMUTING_BLOCKS')
    for term in pauli_sum.terms:
        prog += Pragma('BLOCK')
        prog += exponential_map(term)(param)
        prog += Pragma('END_BLOCK')
    prog += Pragma('END_COMMUTING_BLOCKS')
    return prog
Esempio n. 13
0
    def pre_measure(self):
        """Run the QVM"""
        # pylint: disable=attribute-defined-outside-init
        for e in self.obs_queue:
            wires = e.wires

            if e.name in [
                    "PauliX", "PauliY", "PauliZ", "Identity", "Hadamard"
            ]:
                self.pre_rotations(e.name, wires)

            elif e.name == "Hermitian":
                # For arbitrary Hermitian matrix H, let U be the unitary matrix
                # that diagonalises it, and w_i be the eigenvalues.
                H = e.parameters[0]
                Hkey = tuple(H.flatten().tolist())

                if Hkey in self._eigs:
                    # retrieve eigenvectors
                    U = self._eigs[Hkey]["eigvec"]
                else:
                    # store the eigenvalues corresponding to H
                    # in a dictionary, so that they do not need to
                    # be calculated later
                    w, U = np.linalg.eigh(H)
                    self._eigs[Hkey] = {"eigval": w, "eigvec": U}

                # Perform a change of basis before measuring by applying U^ to the circuit
                self.apply("QubitUnitary", wires, [U.conj().T])

        prag = Program(Pragma("INITIAL_REWIRING", ['"PARTIAL"']))

        if self.active_reset:
            prag += RESET()

        self.prog = prag + self.prog

        qubits = sorted(self.prog.get_qubits())
        ro = self.prog.declare("ro", "BIT", len(qubits))
        for i, q in enumerate(qubits):
            self.prog.inst(MEASURE(q, ro[i]))

        self.prog.wrap_in_numshots_loop(self.shots)

        if "pyqvm" in self.qc.name:
            bitstring_array = self.qc.run(self.prog)
        else:
            self.compiled = self.qc.compile(self.prog)
            bitstring_array = self.qc.run(executable=self.compiled)

        self.state = {}
        for i, q in enumerate(qubits):
            self.state[q] = bitstring_array[:, i]
Esempio n. 14
0
    def __init__(self, path: list, test_type: BellTestType, add_measurements, num_shots):
        super().__init__()

        assert len(path) >= 2, "qubit path has to have length at least 2"

        qubit_a = path[0]
        qubit_b = path[-1]

        self.qubit_a = qubit_a
        self.qubit_b = qubit_b
        self.path = path
        self.test_type = test_type
        self.add_measurements = add_measurements
        self.num_shots = num_shots

        # Build the circuit
        program = pq.Program()
        program += Pragma("INITIAL_REWIRING", ['"NAIVE"'])

        program += pq.gates.X(qubit_a)
        program += pq.gates.X(qubit_b)
        program += pq.gates.H(qubit_a)

        # CNOT along path
        for (x, y) in zip(path[:-2], path[1:-1]):
            program += pq.gates.CNOT(x, y)

        # CNOT the last pair
        program += pq.gates.CNOT(path[-2], qubit_b)

        # undo CNOT along path
        for (x, y) in reversed(list(zip(path[:-2], path[1:-1]))):
            program += pq.gates.CNOT(x, y)

        # measurement directions
        angle_a, angle_b = test_type.value
        if angle_a != 0:
            program += pq.gates.RZ(angle_a, qubit_a)
        if angle_b != 0:
            program += pq.gates.RZ(angle_b, qubit_b)

        # final hadamards
        program += pq.gates.H(qubit_a)
        program += pq.gates.H(qubit_b)

        # store the resulting circuit
        self.program = program
Esempio n. 15
0
def test_pragma_with_placeholders():
    q = QubitPlaceholder()
    q2 = QubitPlaceholder()
    p = Program()
    p.inst(Pragma('FENCE', [q, q2]))
    address_map = {q: 0, q2: 1}
    addressed_pragma = address_qubits(p, address_map)[0]
    parse_equals('PRAGMA FENCE 0 1\n', addressed_pragma)

    pq = Program(X(q))
    pq.define_noisy_readout(q, .8, .9)

    pq.inst(X(q2))
    pq.define_noisy_readout(q2, .9, .8)

    ret = address_qubits(pq, address_map).out()
    assert ret == """X 0
Esempio n. 16
0
    def apply(self, operations, **kwargs):
        """Run the QVM"""
        # pylint: disable=attribute-defined-outside-init
        super().apply(operations, **kwargs)

        prag = Program(Pragma("INITIAL_REWIRING", ['"PARTIAL"']))

        if self.active_reset:
            prag += RESET()

        self.prog = prag + self.prog

        qubits = sorted(self.wiring.values())
        ro = self.prog.declare("ro", "BIT", len(qubits))
        for i, q in enumerate(qubits):
            self.prog.inst(MEASURE(q, ro[i]))

        self.prog.wrap_in_numshots_loop(self.shots)
Esempio n. 17
0
def test_estimate_assignment_probs():
    cxn = Mock(spec=QVMConnection)
    trials = 100
    p00 = .8
    p11 = .75
    cxn.run.side_effect = [[[0]] * int(round(p00 * trials)) +
                           [[1]] * int(round((1 - p00) * trials)),
                           [[1]] * int(round(p11 * trials)) +
                           [[0]] * int(round((1 - p11) * trials))]
    ap_target = np.array([[p00, 1 - p11], [1 - p00, p11]])

    povm_pragma = Pragma("READOUT-POVM",
                         (0, "({} {} {} {})".format(*ap_target.flatten())))
    ap = estimate_assignment_probs(0, trials, cxn, Program(povm_pragma))
    assert np.allclose(ap, ap_target)
    for call in cxn.run.call_args_list:
        args, kwargs = call
        prog = args[0]
        assert prog._instructions[0] == povm_pragma
Esempio n. 18
0
def test_estimate_assignment_probs(mocker: MockerFixture):
    mock_qc = mocker.patch("pyquil.api.QuantumComputer").return_value
    mock_compiler = mocker.patch(
        "pyquil.api._abstract_compiler.AbstractCompiler").return_value

    trials = 100
    p00 = 0.8
    p11 = 0.75

    mock_compiler.native_quil_to_executable.return_value = Program()
    mock_qc.compiler = mock_compiler
    mock_qc
    mock_qc.run.side_effect = [
        QAMExecutionResult(executable=None,
                           readout_data={
                               'ro':
                               np.array([[0]]) * int(round(p00 * trials)) +
                               np.array([[1]]) * int(round((1 - p00) * trials))
                           }),  # I gate results
        QAMExecutionResult(executable=None,
                           readout_data={
                               'ro':
                               np.array([[1]]) * int(round(p11 * trials)) +
                               np.array([[0]]) * int(round((1 - p11) * trials))
                           }),  # X gate results
    ]
    ap_target = np.array([[p00, 1 - p11], [1 - p00, p11]])

    povm_pragma = Pragma("READOUT-POVM",
                         (0, "({} {} {} {})".format(*ap_target.flatten())))
    ap = estimate_assignment_probs(0, trials, mock_qc, Program(povm_pragma))

    assert mock_compiler.native_quil_to_executable.call_count == 2
    assert mock_qc.run.call_count == 2

    for call in mock_compiler.native_quil_to_executable.call_args_list:
        args, kwargs = call
        prog = args[0]
        assert prog._instructions[0] == povm_pragma

    assert np.allclose(ap, ap_target)
Esempio n. 19
0
def test_pragma_with_placeholders():
    q = QubitPlaceholder()
    q2 = QubitPlaceholder()
    p = Program()
    p.inst(Pragma("FENCE", [q, q2]))
    address_map = {q: 0, q2: 1}
    addressed_pragma = address_qubits(p, address_map)[0]
    parse_equals("PRAGMA FENCE 0 1\n", addressed_pragma)

    pq = Program(X(q))
    pq.define_noisy_readout(q, 0.8, 0.9)

    pq.inst(X(q2))
    pq.define_noisy_readout(q2, 0.9, 0.8)

    ret = address_qubits(pq, address_map).out()
    assert (ret == """X 0
PRAGMA READOUT-POVM 0 "(0.8 0.09999999999999998 0.19999999999999996 0.9)"
X 1
PRAGMA READOUT-POVM 1 "(0.9 0.19999999999999996 0.09999999999999998 0.8)"
""")
Esempio n. 20
0
def test_fail_on_bad_pragmas():
    "Check that to_latex raises an error when pragmas are imbalanced."
    # missing END_LATEX_GATE_GROUP
    with pytest.raises(ValueError):
        _ = to_latex(Program(Pragma("LATEX_GATE_GROUP", [], 'foo'), X(0)))

    # missing LATEX_GATE_GROUP
    with pytest.raises(ValueError):
        _ = to_latex(Program(X(0), Pragma("END_LATEX_GATE_GROUP")))

    # nested groups are not currently supported
    with pytest.raises(ValueError):
        _ = to_latex(
            Program(Pragma("LATEX_GATE_GROUP"), X(0),
                    Pragma("LATEX_GATE_GROUP"), X(1),
                    Pragma("END_LATEX_GATE_GROUP"),
                    Pragma("END_LATEX_GATE_GROUP")))
def adder(num_a: Sequence[int],
          num_b: Sequence[int],
          register_a: Sequence[int],
          register_b: Sequence[int],
          carry_ancilla: int,
          z_ancilla: int,
          in_x_basis: bool = False,
          use_param_program: bool = False) -> Program:
    """
    Produces a program implementing reversible adding on a quantum computer to compute a + b.

    This implementation is based on [CDKM96]_, which is easy to implement, if not the most
    efficient. Each register of qubit labels should be provided such that the first qubit in
    each register is expected to carry the least significant bit of the respective number. This
    method also requires two extra ancilla, one initialized to 0 that acts as a dummy initial
    carry bit and another (which also probably ought be initialized to 0) that stores the most
    significant bit of the addition (should there be a final carry). The most straightforward
    ordering of the registers and two ancilla for adding n-bit numbers follows the pattern::

        carry_ancilla
        b_0
        a_0
        ...
        b_j
        a_j
        ...
        b_n
        a_n
        z_ancilla

    With this layout, all gates in the circuit act on sets of three adjacent qubits. Such a
    layout is provided by calling get_qubit_registers_for_adder on the quantum resource. Note
    that even with this layout some of the gates used to implement the circuit may not be native.
    In particular there are CCNOT gates which must be decomposed and CNOT(q1, q3) gates acting on
    potentially non-adjacenct qubits (the layout only ensures q2 is adjacent to both q1 and q3).

    The output of the circuit falls on the qubits initially labeled by the b bits (and z_ancilla).

    The default option is to compute the addition in the computational (aka Z) basis. By setting
    in_x_basis true, the gates :func:`primitives.CNOT_X_basis` and :func:`primitives.CCNOT_X_basis`
    will replace CNOT and CCNOT so that the computation happens in the X basis.

    .. [CDKM96] "A new quantum ripple-carry addition circuit"
        S. Cuccaro, T. Draper, s. Kutin, D. Moulton
        https://arxiv.org/abs/quant-ph/0410184

    :param num_a: the bitstring representation of the number a with least significant bit last
    :param num_b: the bitstring representation of the number b with least significant bit last
    :param register_a: list of qubit labels for register a, with least significant bit labeled first
    :param register_b: list of qubit labels for register b, with least significant bit labeled first
    :param carry_ancilla: qubit labeling a zero-initialized qubit, ideally adjacent to b_0
    :param z_ancilla: qubit label, a zero-initialized qubit, ideally adjacent to register_a[-1]
    :param in_x_basis: if true, prepare the bitstring-representation of the numbers in the x basis
        and subsequently performs all addition logic in the x basis.
    :param use_param_program: if true, the input num_a and num_b should be arrays of the proper
        length, but their contents will be disregarded. Instead, the program returned will be
        parameterized and the input bitstrings to add must be specified at run time.
    :return: pyQuil program that implements the addition a+b, with output falling on the qubits
        formerly storing the input b. The output of a measurement will list the lsb as the last bit.
    """
    if len(num_a) != len(num_b):
        raise ValueError("Numbers being added must be equal length bitstrings")

    # First, generate a set preparation program in the desired basis.
    prog = Program(Pragma('PRESERVE_BLOCK'))
    if use_param_program:
        input_register = register_a + register_b
        prog += parameterized_bitstring_prep(input_register[::-1],
                                             REG_NAME,
                                             in_x_basis=in_x_basis)
    else:
        prog += bitstring_prep(register_a, num_a[::-1], in_x_basis=in_x_basis)
        prog += bitstring_prep(register_b, num_b[::-1], in_x_basis=in_x_basis)

    if in_x_basis:
        prog += [H(carry_ancilla), H(z_ancilla)]

    # preparation complete; end the preserve block
    prog += Pragma("END_PRESERVE_BLOCK")

    prog_to_rev = Program()
    current_carry_label = carry_ancilla
    for (a, b) in zip(register_a, register_b):
        prog += majority_gate(a, b, current_carry_label, in_x_basis)
        prog_to_rev += unmajority_add_gate(a, b, current_carry_label,
                                           in_x_basis).dagger()
        current_carry_label = a

    undo_and_add_prog = prog_to_rev.dagger()
    if in_x_basis:
        prog += CNOT_X_basis(register_a[-1], z_ancilla)
        # need to switch back to computational (z) basis before measuring
        for qubit in register_b:  # answer lays on the b qubit register
            undo_and_add_prog.inst(H(qubit))
        undo_and_add_prog.inst(H(z_ancilla))
    else:
        prog += CNOT(register_a[-1], z_ancilla)
    prog += undo_and_add_prog

    ro = prog.declare('ro', memory_type='BIT', memory_size=len(register_b) + 1)
    for idx, qubit in enumerate(register_b):
        prog += MEASURE(qubit, ro[len(register_b) - idx])
    prog += MEASURE(z_ancilla, ro[0])

    return prog
Esempio n. 22
0
    def simulate(self, amplitudes):
        """Perform the simulation for the molecule.

        Args:
            amplitudes (list): The initial amplitudes (float64).
        Returns:
            float64: The total energy (energy).
        Raise:
            ValueError: If the dimension of the amplitude list is incorrect.
        """
        if len(amplitudes) != self.amplitude_dimension:
            raise ValueError("Incorrect dimension for amplitude list.")

        #Anti-hermitian operator and its qubit form
        generator = uccsd_singlet_generator(amplitudes, self.of_mole.n_qubits,
                                            self.of_mole.n_electrons)
        jw_generator = jordan_wigner(generator)
        pyquil_generator = qubitop_to_pyquilpauli(jw_generator)

        p = Program(Pragma('INITIAL_REWIRING', ['"GREEDY"']))
        # Set initial wavefunction (Hartree-Fock)
        for i in range(self.of_mole.n_electrons):
            p.inst(X(i))
        # Trotterization (unitary for UCCSD state preparation)
        for term in pyquil_generator.terms:
            term.coefficient = np.imag(term.coefficient)
            p += exponentiate(term)
        p.wrap_in_numshots_loop(self.backend_options["n_shots"])

        #  Do not simulate if no operator was passed
        if len(self.qubit_hamiltonian.terms) == 0:
            return 0.
        else:
            # Run computation using the right backend
            if isinstance(self.backend_options["backend"],
                          WavefunctionSimulator):
                energy = self.backend_options["backend"].expectation(
                    prep_prog=p, pauli_terms=self.forest_qubit_hamiltonian)
            else:
                # Set up experiment, each setting corresponds to a particular measurement basis
                settings = [
                    ExperimentSetting(in_state=TensorProductState(),
                                      out_operator=forest_term)
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                experiment = TomographyExperiment(settings=settings, program=p)
                print(experiment, "\n")
                results = self.backend_options["backend"].experiment(
                    experiment)

                energy = 0.
                coefficients = [
                    forest_term.coefficient
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                for i in range(len(results)):
                    energy += results[i].expectation * coefficients[i]

            energy = np.real(energy)

        # Save the amplitudes so we have the optimal ones for RDM calculation
        self.optimized_amplitudes = amplitudes

        return energy
Esempio n. 23
0
import numpy as np
from pyquil import get_qc
from pyquil.quil import Program, Pragma
from pyquil.gates import RX, RZ, X
import tqdm

# Get our QuantumComputer instance, with a Quantum Virutal Machine (QVM) backend
qpu_name = "8q-qvm"  # 8q-qvm   Aspen-8
asqvm = True
QPU = get_qc(qpu_name, as_qvm=asqvm)

calibration_trials = 2**10
measurement_trials = 2**10
batches = 2**2

program_initialization = Program(Pragma('INITIAL_REWIRING', ['"NAIVE"']))
"""
defining ansatz
"""


def calibration_ansatz(state, prog_in=program_initialization):
    prog_out = prog_in.copy()
    if state == 1:
        prog_out += X(0)
    return prog_out


def ansatz(angle, prog_in=program_initialization):
    prog_out = prog_in.copy()
    prog_out += RX(angle, 0)
Esempio n. 24
0
def _naive_program_generator(qc: QuantumComputer, qubits: Sequence[int],
                             permutations: Sequence[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.
    """
    # artificially restrict the entire computation to num_measure_qubits
    num_measure_qubits = len(permutations[0])
    # if these measure_qubits do not have a topology that supports the program, the compiler may
    # act on a different (potentially larger) subset of the input sequence of qubits.
    measure_qubits = qubits[:num_measure_qubits]

    # create a simple program that uses the compiler to directly generate 2q gates from the matrices
    prog = Program(Pragma('INITIAL_REWIRING', ['"PARTIAL"']))
    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(measure_qubits[perm[gate_idx]]),
                      int(measure_qubits[perm[gate_idx + 1]]))

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

    # restrict compilation to chosen qubits
    isa_dict = qc.device.get_isa().to_dict()
    single_qs = isa_dict['1Q']
    two_qs = isa_dict['2Q']

    new_1q = {}
    for key, val in single_qs.items():
        if int(key) in qubits:
            new_1q[key] = val
    new_2q = {}
    for key, val in two_qs.items():
        q1, q2 = key.split('-')
        if int(q1) in qubits and int(q2) in qubits:
            new_2q[key] = val

    new_isa = {'1Q': new_1q, '2Q': new_2q}

    new_compiler = copy(qc.compiler)
    new_compiler.target_device = TargetDevice(
        isa=new_isa, specs=qc.device.get_specs().to_dict())
    # try to compile with the restricted qubit topology
    try:
        native_quil = new_compiler.quil_to_native_quil(prog)
    except RPCErrorError as e:
        if "Multiqubit instruction requested between disconnected components of the QPU graph:" \
                in str(e):
            raise ValueError(
                "naive_program_generator could not generate a program using only the "
                "qubits supplied; expand the set of allowed qubits or supply "
                "a custom program_generator.")
        raise

    return native_quil
Esempio n. 25
0
def convert_dds_to_pyquil_program(dynamic_decoupling_sequence,
                                  target_qubits=None,
                                  gate_time=0.1,
                                  add_measurement=True,
                                  algorithm=INSTANT_UNITARY):
    """Converts a Dynamic Decoupling Sequence into quantum program
    as defined in pyQuil.

    Parameters
    ----------
    dynamic_decoupling_sequence : DynamicDecouplingSequence
        The dynamic decoupling sequence
    target_qubits : list, optional
        List of integers specifying target qubits for the sequence operation;
        defaults to None in which case 0-th Qubit is used
    gate_time : float, optional
        Time (in seconds) delay introduced by a gate; defaults to 0.1
    add_measurement : bool, optional
        If True, the circuit contains a measurement operation for each of the
        target qubits and a set of ClassicalRegister objects created with length
        equal to `len(target_qubits)`
    algorithm : str, optional
        One of 'fixed duration unitary' or 'instant unitary'; In the case of
        'fixed duration unitary', the sequence operations are assumed to be
        taking the amount of gate_time while 'instant unitary' assumes the sequence
        operations are instantaneous (and hence does not contribute to the delay between
        offsets). Defaults to 'instant unitary'.

    Returns
    -------
    pyquil.Program
        The pyQuil program containing gates specified by the rotations of
        dynamic decoupling sequence

    Raises
    ------
    ArgumentsValueError
        If any of the input parameters are invalid
    Notes
    -----
    Dynamic Decoupling Sequences (DDS) consist of idealized pulse operation. Theoretically,
    these operations (pi-pulses in X,Y or Z) occur instantaneously. However, in practice,
    pulses require time. Therefore, this method of converting an idealized sequence
    results to a circuit that is only an approximate implementation of the idealized sequence.
    In idealized definition of DDS, `offsets` represents the instances within sequence
    `duration` where a pulse occurs instantaneously. A series of appropriate gatges
    is placed in order to represent these pulses. The `gaps` or idle time in between active
    pulses are filled up with `identity` gates. Each identity gate introduces a delay of
    `gate_time`. In this implementation, the number of identity gates is determined by
    :math:`np.int(np.floor(offset_distance / gate_time))`. As a consequence, the duration of
    the real-circuit is :math:`gate_time \\times number_of_identity_gates +
    pulse_gate_time \\times number_of_pulses`.
    Q-CTRL Open Controls support operation resulting in rotation around at most one axis at
    any offset.
    """

    if dynamic_decoupling_sequence is None:
        raise ArgumentsValueError(
            'No dynamic decoupling sequence provided.',
            {'dynamic_decoupling_sequence': dynamic_decoupling_sequence})

    if not isinstance(dynamic_decoupling_sequence, DynamicDecouplingSequence):
        raise ArgumentsValueError(
            'Dynamical decoupling sequence is not recognized.'
            'Expected DynamicDecouplingSequence instance', {
                'type(dynamic_decoupling_sequence)':
                type(dynamic_decoupling_sequence)
            })

    target_qubits = target_qubits or [0]

    if gate_time <= 0:
        raise ArgumentsValueError(
            'Time delay of identity gate must be greater than zero.',
            {'gate_time': gate_time})

    if np.any(target_qubits) < 0:
        raise ArgumentsValueError(
            'Every target qubits index must be non-negative.',
            {'target_qubits': target_qubits})

    if algorithm not in [FIX_DURATION_UNITARY, INSTANT_UNITARY]:
        raise ArgumentsValueError(
            'Algorithm must be one of {} or {}'.format(INSTANT_UNITARY,
                                                       FIX_DURATION_UNITARY),
            {'algorithm': algorithm})

    unitary_time = 0.
    if algorithm == FIX_DURATION_UNITARY:
        unitary_time = gate_time

    rabi_rotations = dynamic_decoupling_sequence.rabi_rotations
    azimuthal_angles = dynamic_decoupling_sequence.azimuthal_angles
    detuning_rotations = dynamic_decoupling_sequence.detuning_rotations

    offsets = dynamic_decoupling_sequence.offsets

    time_covered = 0
    program = Program()
    program += Pragma('PRESERVE_BLOCK')

    for offset, rabi_rotation, azimuthal_angle, detuning_rotation in zip(
            list(offsets), list(rabi_rotations), list(azimuthal_angles),
            list(detuning_rotations)):

        offset_distance = offset - time_covered

        if np.isclose(offset_distance, 0.0):
            offset_distance = 0.0

        if offset_distance < 0:
            raise ArgumentsValueError(
                "Offsets cannot be placed properly. Spacing between the rotations"
                "is smaller than the time required to perform the rotation. Provide"
                "a longer dynamic decoupling sequence or shorted gate time.", {
                    'dynamic_decoupling_sequence': dynamic_decoupling_sequence,
                    'gate_time': gate_time
                })

        while (time_covered + gate_time) <= offset:
            for qubit in target_qubits:
                program += I(qubit)
            time_covered += gate_time

        x_rotation = rabi_rotation * np.cos(azimuthal_angle)
        y_rotation = rabi_rotation * np.sin(azimuthal_angle)
        z_rotation = detuning_rotation

        rotations = np.array([x_rotation, y_rotation, z_rotation])
        zero_pulses = np.isclose(rotations, 0.0).astype(np.int)
        nonzero_pulse_counts = 3 - np.sum(zero_pulses)
        if nonzero_pulse_counts > 1:
            raise ArgumentsValueError(
                'Open Controls support a sequence with one '
                'valid rotation at any offset. Found a sequence '
                'with multiple rotation operations at an offset.',
                {'dynamic_decoupling_sequence': dynamic_decoupling_sequence},
                extras={
                    'offset': offset,
                    'rabi_rotation': rabi_rotation,
                    'azimuthal_angle': azimuthal_angle,
                    'detuning_rotation': detuning_rotation
                })

        for qubit in target_qubits:
            if nonzero_pulse_counts == 0:
                program += I(qubit)
            else:
                if not np.isclose(rotations[0], 0.0):
                    program += RX(rotations[0], qubit)
                elif not np.isclose(rotations[1], 0.0):
                    program += RY(rotations[1], qubit)
                elif not np.isclose(rotations[2], 0.):
                    program += RZ(rotations[2], qubit)

        time_covered = offset + unitary_time

    if add_measurement:
        readout = program.declare('ro', 'BIT', len(target_qubits))
        for idx, qubit in enumerate(target_qubits):
            program += MEASURE(qubit, readout[idx])

    program += Pragma('END_PRESERVE_BLOCK')

    return program
Esempio n. 26
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