def amplification_circuit(algorithm, oracle, qubits, num_iter): """ Returns a program that does ``num_iter`` rounds of amplification, given a measurement-less algorithm, an oracle, and a list of qubits to operate on. :param Program algorithm: A program representing a measurement-less algorithm run on qubits. :param Program oracle: An oracle maps any basis vector ``|psi>`` to either ``+|psi>`` or ``-|psi>`` depending on whether ``|psi>`` is in the desirable subspace or the undesirable subspace. :param Sequence qubits: the qubits to operate on :param int num_iter: number of iterations of amplifications to run :return: The amplified algorithm. :rtype: Program """ if num_iter <= 0: raise ValueError("num_iter must be greater than zero") prog = pq.Program() uniform_superimposer = pq.Program().inst([H(qubit) for qubit in qubits]) prog += uniform_superimposer for _ in range(num_iter): prog += oracle + algorithm.dagger() + diffusion_program(qubits) + algorithm return prog
def _construct_deutsch_jozsa_circuit(self): """ Builds the Deutsch-Jozsa circuit. Which can determine whether a function f mapping :math:`\{0,1\}^n \to \{0,1\}` is constant or balanced, provided that it is one of them. :return: A program corresponding to the desired instance of Simon's Algorithm. :rtype: Program """ dj_prog = pq.Program() # Put the first ancilla qubit (query qubit) into minus state dj_prog.inst(X(self.ancillas[0]), H(self.ancillas[0])) # Apply Hadamard, Oracle, and Hadamard again dj_prog.inst([H(qubit) for qubit in self.computational_qubits]) # Build the oracle oracle_prog = pq.Program() oracle_prog.defgate(ORACLE_GATE_NAME, self.unitary_matrix) scratch_bit = self.ancillas[1] qubits_for_funct = [scratch_bit] + self.computational_qubits oracle_prog.inst(tuple([ORACLE_GATE_NAME] + qubits_for_funct)) dj_prog += oracle_prog # Here the oracle does not leave the computational qubits unchanged, so we use a CNOT to # to move the result to the query qubit, and then we uncompute with the dagger. dj_prog.inst(CNOT(self._qubits[0], self.ancillas[0])) dj_prog += oracle_prog.dagger() dj_prog.inst([H(qubit) for qubit in self.computational_qubits]) return dj_prog
def meyer_penny_program(): """ Returns the program to simulate the Meyer-Penny Game :return: pyQuil Program """ picard_register = 1 answer_register = 0 then_branch = pq.Program(X(0)) else_branch = pq.Program(I(0)) return (pq.Program() # Prepare Qubits in Heads state or superposition, respectively .inst(X(0), H(1)) # Q puts the coin into a superposition .inst(H(0)) # Picard makes a decision and acts accordingly .measure(1, picard_register) .if_then(picard_register, then_branch, else_branch) # Q undoes his superposition operation .inst(H(0)) # The outcome is recorded into the answer register .measure(0, answer_register))
def meyer_penny_program(): """ Returns the program to simulate the Meyer-Penny Game The full description is available in ../docs/source/exercises.rst :return: pyQuil Program """ prog = pq.Program() ro = prog.declare("ro", memory_size=2) picard_register = ro[1] answer_register = ro[0] then_branch = pq.Program(X(0)) else_branch = pq.Program(I(0)) # Prepare Qubits in Heads state or superposition, respectively prog.inst(X(0), H(1)) # Q puts the coin into a superposition prog.inst(H(0)) # Picard makes a decision and acts accordingly prog.measure(1, picard_register) prog.if_then(picard_register, then_branch, else_branch) # Q undoes his superposition operation prog.inst(H(0)) # The outcome is recorded into the answer register prog.measure(0, answer_register) return prog
def state_prep_classical(beta_value, start_qb): """ Alternative state prep. Effectively creates an initial thermal state by clasically sampling a distribution and feeding that into the circuit. rho_m = exp(-beta*H_M)/Z_m Input: beta_value: Inverse temperature to the initial qubits start_qb: Initialize on a specific qubit number to avoid dead qubits on Rigetti Output: state_prep: A Rigetti Quil program for the initial state -------X^(classical outcome on qubit j)----- H ------- """ state_prep = pq.Program() for i in range(start_qb, num_good_qubits + start_qb, 1): tmp = pq.Program() samp = classical_sample(beta_value) if samp == 0: tmp.inst(H(i)) else: tmp.inst(X(i), H(i)) state_prep = state_prep + tmp return state_prep
def weights_cost(nu_value, weights): """ Implements exponential of 2-local terms (ZZ) of full cost Hamiltonian. Input: nu_value: pulse parameter weights: J_jk weights across all qubit pairs Output: weights_cost: Quil program creating weight cost Hamiltonian """ assert (max(len(weights), len(weights[0])) <= num_good_qubits) weights_cost = pq.Program() for j in range(0, len(weights), 1): for k in range(0, len(weights[0]), 1): tmp = pq.Program() if j != k: tmp.inst(CNOT(j, k), RZ(2.0 * nu_value * weights[j][k], k), CNOT(j, k)) else: tmp.inst(RZ(2.0 * nu_value * weights[j][k], k)) weights_cost = weights_cost + tmp return weights_cost
def _exponentiate_general_case(pauli_term, param): """ Returns a Quil (Program()) object corresponding to the exponential of the pauli_term object, i.e. exp[-1.0j * param * pauli_term] :param pauli_term: (PauliTerm) to exponentiate :param param: scalar, non-complex, value :returns: A Quil (Program()) object """ def reverse_hack(p): # A hack to produce a *temporary* program which reverses p. def translate(tup): action, obj = tup if tup == pqb.ACTION_RELEASE_QUBIT: return (pqb.ACTION_INSTANTIATE_QUBIT, obj) elif tup == pqb.ACTION_INSTANTIATE_QUBIT: return (pqb.ACTION_RELEASE_QUBIT, obj) else: return tup revp = pq.Program() revp.actions = map(translate, reversed(p.actions)) return revp quil_prog = pq.Program() change_to_z_basis = pq.Program() change_to_original_basis = pq.Program() cnot_seq = pq.Program() prev_index = None highest_target_index = None for index, op in pauli_term: if 'X' == op: change_to_z_basis.inst(H(index)) change_to_original_basis.inst(H(index)) elif 'Y' == op: change_to_z_basis.inst(RX(np.pi / 2.0)(index)) change_to_original_basis.inst(RX(-np.pi / 2.0)(index)) elif 'I' == op: continue if prev_index is not None: cnot_seq.inst(CNOT(prev_index, index)) prev_index = index highest_target_index = index # building rotation circuit quil_prog += change_to_z_basis quil_prog += cnot_seq quil_prog.inst( RZ(2.0 * pauli_term.coefficient * param)(highest_target_index)) quil_prog += reverse_hack(cnot_seq) quil_prog += change_to_original_basis return quil_prog
def _recursive_builder(self, operation, gate_name, control_qubits, target_qubit): """Helper function used to define the controlled gate recursively. It uses the algorithm in the reference above. Namely it recursively constructs a controlled gate by applying a controlled square root of the gate, followed by a toffoli gate with len(control_qubits) - 1 controls, applying the controlled adjoint of the square root, another toffoli with len(control_qubits) - 1 controls, and finally another controlled copy of the gate. :param numpy.ndarray operation: The matrix for the unitary to be controlled. :param String gate_name: The name for the gate being controlled. :param Sequence control_qubits: The qubits that are the controls. :param Qubit or Int target_qubit: The qubit that the gate should be applied to. :return: The intermediate Program being built. :rtype: Program """ control_true = np.kron(ONE_PROJECTION, operation) control_false = np.kron(ZERO_PROJECTION, np.eye(2, 2)) control_root_true = np.kron(ONE_PROJECTION, sqrtm(operation, disp=True)) controlled_gate = control_true + control_false controlled_root_gate = control_root_true + control_false sqrt_name = self.format_gate_name(SQRT_PREFIX, gate_name) controlled_subprogram = pq.Program() control_gate = pq.Program() # For the base case, we check to see if there is just one control qubit. # If it is a CNOT we explicitly break the naming convention so as not to redefine the gate. if len(control_qubits) == 1: if gate_name == NOT_GATE_LABEL: control_name = CONTROL_PREFIX + gate_name else: control_name = self.format_gate_name(CONTROL_PREFIX, gate_name) control_gate = self._defgate(control_gate, control_name, controlled_gate) control_gate.inst((control_name, control_qubits[0], target_qubit)) return control_gate else: control_sqrt_name = self.format_gate_name(CONTROL_PREFIX, sqrt_name) control_gate = self._defgate(control_gate, control_sqrt_name, controlled_root_gate) control_gate.inst( (control_sqrt_name, control_qubits[-1], target_qubit)) # Here we recurse to build a toffoli gate on n - 1 of the qubits. n_minus_one_toffoli = self._recursive_builder( NOT_GATE, NOT_GATE_LABEL, control_qubits[:-1], control_qubits[-1]) # We recurse to build a controlled sqrt of the target_gate, excluding the last control. n_minus_one_controlled_sqrt = self._recursive_builder( sqrtm(operation, disp=True), sqrt_name, control_qubits[:-1], target_qubit) controlled_subprogram += control_gate controlled_subprogram += n_minus_one_toffoli controlled_subprogram += control_gate.dagger() # We only add the instructions so that we don't redefine gates controlled_subprogram += n_minus_one_toffoli.instructions controlled_subprogram += n_minus_one_controlled_sqrt return controlled_subprogram
def test_simple_inverse_qft(): trial_prog = pq.Program() trial_prog.inst(X(0)) trial_prog = trial_prog + inverse_qft([0]) result_prog = pq.Program().inst([X(0), H(0)]) compare_progs(trial_prog, result_prog)
def test_simple_inverse_qft(): trial_prog = pq.Program() trial_prog.inst(X(0)) trial_prog = trial_prog + inverse_qft([0]) result_prog = pq.Program().inst([X(0), H(0)]) assert trial_prog == result_prog
def initialize_system(ancilla_qubit): """ Prepare initial state :param list ancilla_qubit: Qubit of ancilla register. :return Program p_ic: Quil program to initialize this system. """ # ancilla qubit to plane wave state ic_ancilla = pq.Program([X(ancilla_qubit), H(ancilla_qubit)]) p_ic = pq.Program(ic_ancilla) return p_ic
def trotterize(first_pauli_term, second_pauli_term, trotter_order=1, trotter_steps=1): """ Create a Quil program that approximates exp( (A + B)t) where A and B are PauliTerm operators. :param PauliTerm first_pauli_term: PauliTerm denoted `A` :param PauliTerm second_pauli_term: PauliTerm denoted `B` :param int trotter_order: Optional argument indicating the Suzuki-Trotter approximation order--only accepts orders 1, 2, 3, 4. :param int trotter_steps: Optional argument indicating the number of products to decompose the exponential into. :return: Quil program :rtype: Program """ if not (1 <= trotter_order < 5): raise ValueError( "trotterize only accepts trotter_order in {1, 2, 3, 4}.") commutator = (first_pauli_term * second_pauli_term) +\ (-1 * second_pauli_term * first_pauli_term) prog = pq.Program() fused_param_prog = ParametricProgram(lambda: pq.Program()) if is_zero(commutator): param_exp_prog_one = exponential_map(first_pauli_term) exp_prog = param_exp_prog_one(1) prog += exp_prog param_exp_prog_two = exponential_map(second_pauli_term) exp_prog = param_exp_prog_two(1) prog += exp_prog fused_param_prog = param_exp_prog_one.fuse(param_exp_prog_two) return prog, fused_param_prog order_slices = suzuki_trotter(trotter_order, trotter_steps) for coeff, operator in order_slices: if operator == 0: param_prog = exponential_map(coeff * first_pauli_term) exp_prog = param_prog(1) fused_param_prog = fused_param_prog.fuse(param_prog) prog += exp_prog else: param_prog = exponential_map(coeff * second_pauli_term) exp_prog = param_prog(1) fused_param_prog = fused_param_prog.fuse(param_prog) prog += exp_prog return prog, fused_param_prog
def test_multi_qubit_qft(): trial_prog = pq.Program() trial_prog.inst(X(0), X(1), X(2)) trial_prog = trial_prog + inverse_qft([0, 1, 2]) result_prog = pq.Program().inst([X(0), X(1), X(2), SWAP(0, 2), H(0), CPHASE(-1.5707963267948966, 0, 1), CPHASE(-0.7853981633974483, 0, 2), H(1), CPHASE(-1.5707963267948966, 1, 2), H(2)]) assert trial_prog == result_prog
def grover(oracle, qubits, query_qubit, num_iter=None): """ Implementation of Grover's Algorithm for a given oracle. The query qubit will be left in the zero state afterwards. :param oracle: An oracle defined as a Program. It should send |x>|q> to |x>|q \oplus f(x)>, where |q> is a a query qubit, and the range of f is {0, 1}. :param qubits: List of qubits for Grover's Algorithm. The last is assumed to be query for the oracle. :param query_qubit: The qubit |q> that the oracle write its answer to. :param num_iter: The number of iterations to repeat the algorithm for. The default is int(pi(sqrt(N))/4. :return: A program corresponding to the desired instance of Grover's Algorithm. """ if len(qubits) < 1: raise ValueError("Grover's Algorithm requires at least 1 qubits.") if num_iter is None: num_iter = int(round(np.pi * 2**(len(qubits) / 2.0 - 2.0))) diff_op = diffusion_operator(qubits) def_gates = oracle.defined_gates + diff_op.defined_gates unique_gates = [] seen_names = set() for gate in def_gates: if gate.name not in seen_names: seen_names.add(gate.name) unique_gates.append(gate) many_hadamard = pq.Program().inst(map(H, qubits)) grover_iter = oracle + many_hadamard + diff_op + many_hadamard # To prevent redefining gates, this is not the preferred way. grover_iter.defined_gates = [] prog = pq.Program() prog.defined_gates = unique_gates # Initialize ancilla to be in the minus state prog.inst(X(query_qubit)) prog.inst(H(query_qubit)) prog += many_hadamard for _ in xrange(num_iter): prog += grover_iter # Move the ancilla back to the zero state prog.inst(H(query_qubit)) prog.inst(X(query_qubit)) return prog
def get_hhl_2x2(A, b, r, qubits): '''Generate a circuit that implements the full HHL algorithm for the case of 2x2 matrices. :param A: (numpy.ndarray) A Hermitian 2x2 matrix. :param b: (numpy.ndarray) A vector. :param r: (float) Parameter to be tuned in the algorithm. :param verbose: (bool) Optional information about the wavefunction. :return: A Quil program to perform HHL. ''' p = pq.Program() p.inst(create_arbitrary_state(b, [qubits[3]])) p.inst(H(qubits[1])) p.inst(H(qubits[2])) p.defgate('CONTROLLED-U0', controlled(scipy.linalg.expm(2j*π*A/4))) p.inst(('CONTROLLED-U0', qubits[2], qubits[3])) p.defgate('CONTROLLED-U1', controlled(scipy.linalg.expm(2j*π*A/2))) p.inst(('CONTROLLED-U1', qubits[1], qubits[3])) p.inst(SWAP(qubits[1], qubits[2])) p.inst(H(qubits[2])) p.defgate('CSdag', controlled(np.array([[1, 0], [0, -1j]]))) p.inst(('CSdag', qubits[1], qubits[2])) p.inst(H(qubits[1])) p.inst(SWAP(qubits[1], qubits[2])) uncomputation = p.dagger() p.defgate('CRy0', controlled(rY(2*π/2**r))) p.inst(('CRy0', qubits[1], qubits[0])) p.defgate('CRy1', controlled(rY(π/2**r))) p.inst(('CRy1', qubits[2], qubits[0])) p += uncomputation return p
def test_one_qubit_exact_zeros_circuit(): exact_one_qubit_bitmap = {"0": "0", "1": "1"} dj = DeutschJosza() with patch("pyquil.api.JobConnection") as qvm: # Should just be not the zero vector expected_bitstring = np.asarray([0, 1], dtype=int) qvm.run_and_measure.return_value = mock_job_result(expected_bitstring) _ = dj.is_constant(qvm, exact_one_qubit_bitmap) # Ordering doesn't matter, so we pop instructions from a set expected_prog = pq.Program() # We've defined the oracle and its dagger. dj_circuit = dj.deutsch_jozsa_circuit assert len(dj_circuit.defined_gates) == 2 defined_oracle = None defined_oracle_inv = None for gate in dj_circuit.defined_gates: if gate.name == ORACLE_GATE_NAME: defined_oracle = gate else: defined_oracle_inv = gate assert defined_oracle is not None assert defined_oracle_inv is not None expected_prog.defgate(defined_oracle.name, defined_oracle.matrix) expected_prog.defgate(defined_oracle_inv.name, defined_oracle_inv.matrix) expected_prog.inst([ X(1), H(1), H(0), (defined_oracle.name, 2, 0), CNOT(0, 1), (defined_oracle_inv.name, 2, 0), H(0) ]) assert expected_prog.out() == dj.deutsch_jozsa_circuit.out()
def visualize_cost_matrix(qaoa_inst, cost_operators, number_of_qubits, gammas=np.array([1.0]), steps=1): from referenceqvm.api import QVMConnection as debug_QVMConnectiont debug_qvm = debug_QVMConnectiont(type_trans='unitary') param_prog, cost_param_programs = qaoa_inst.get_parameterized_program() import pyquil.quil as pq final_cost_prog = pq.Program() for idx in range(steps): for fprog in cost_param_programs[idx]: final_cost_prog += fprog(gammas[idx]) final_matrix = debug_qvm.unitary(final_cost_prog) costs = np.diag(final_matrix) pure_costs = np.real(np.round(-np.log(costs) * 1j, 3)) for i in range(2**self.number_of_qubits): print(np.binary_repr(i, width=self.number_of_qubits), pure_costs[i], np.round(costs[i], 3)) most_freq_string, sampling_results = qaoa_inst.get_string(betas, gammas, samples=100000) print("Most common results") [print(el) for el in sampling_results.most_common()[:10]] print("Least common results") [print(el) for el in sampling_results.most_common()[-10:]] pdb.set_trace()
def basis_selector_oracle(bitstring, qubits): """ Defines an oracle that selects the ith element of the computational basis. Defines a phase filp rather than bit flip oracle to eliminate need for extra qubit. Flips the sign of the state :math:`\\vert x\\rangle>` if and only if x==bitstring and does nothing otherwise. :param bitstring: The desired bitstring, given as a string of ones and zeros. e.g. "101" :param qubits: The qubits the oracle is called on. The qubits are assumed to be ordered from most significant qubit to least significant qubit. :return: A program representing this oracle. """ if len(qubits) != len(bitstring): raise ValueError( "The bitstring should be the same length as the number of qubits.") if not (isinstance(bitstring, str) and all([num in ('0', '1') for num in bitstring])): raise ValueError("The bitstring must be a string of ones and zeros.") prog = pq.Program() for i, qubit in enumerate(qubits): if bitstring[i] == '0': prog.inst(X(qubit)) prog += amp.n_qubit_control(qubits[:-1], qubits[-1], np.array([[1, 0], [0, -1]]), 'Z') for i, qubit in enumerate(qubits): if bitstring[i] == '0': prog.inst(X(qubit)) return prog
def test_phase_estimation(): phase = 0.75 precision = 4 phase_factor = np.exp(1.0j * 2 * np.pi * phase) U = np.array([[phase_factor, 0], [0, -1 * phase_factor]]) trial_prog = phase_estimation(U, precision) result_prog = pq.Program([H(i) for i in range(precision)]) q_out = range(precision, precision + 1) for i in range(precision): if i > 0: U = np.dot(U, U) cU = controlled(U) name = "CONTROLLED-U{0}".format(2**i) result_prog.defgate(name, cU) result_prog.inst((name, i) + tuple(q_out)) result_prog += inverse_qft(range(precision)) result_prog += [MEASURE(i, [i]) for i in range(precision)] assert (trial_prog == result_prog)
def basis_selector_oracle(bitstring, qubits, query_qubit): """ Defines an oracle that selects the ith element of the computational basis. Sends the state |x>|q> -> |x>|!q> if x==bitstring and |x>|q> otherwise. :param bitstring: The desired bitstring, given as a string of ones and zeros. e.g. "101" :param qubits: The qubits the oracle is called on, the last of which is the query qubit. The qubits are assumed to be ordered from most significant qubit to least signicant qubit. :param query_qubit: The qubit |q> that the oracle write its answer to. :return: A program representing this oracle. """ if len(qubits) != len(bitstring): raise ValueError("The bitstring should be the same length as the number of qubits.") if not isinstance(query_qubit, Qubit): raise ValueError("query_qubit should be a single qubit.") if not (isinstance(bitstring, str) and all([num in ('0', '1') for num in bitstring])): raise ValueError("The bitstring must be a string of ones and zeros.") prog = pq.Program() for i, qubit in enumerate(qubits): if bitstring[i] == '0': prog.inst(X(qubit)) prog += n_qubit_control(qubits, query_qubit, np.array([[0, 1], [1, 0]]), 'NOT') for i, qubit in enumerate(qubits): if bitstring[i] == '0': prog.inst(X(qubit)) return prog
def grover(oracle, qubits, num_iter=None): """ Implementation of Grover's Algorithm for a given oracle. The query qubit will be left in the zero state afterwards. :param Program oracle: An oracle defined as a Program. It should send |x> to (-1)^f(x)|x>, where the range of f is {0, 1}. :param list(int) qubits: List of qubits for Grover's Algorithm. :param int num_iter: The number of iterations to repeat the algorithm for. The default is the integer closest to :math:`\\frac{\\pi}{4}\sqrt{N}`, where :math:`N` is the size of the domain. :return: A program corresponding to the desired instance of Grover's Algorithm. :rtype: Program """ if len(qubits) < 1: raise ValueError("Grover's Algorithm requires at least 1 qubits.") if num_iter is None: num_iter = int(round(np.pi * 2**(len(qubits) / 2.0 - 2.0))) many_hadamard = pq.Program().inst(list(map(H, qubits))) amp_prog = amp.amplify(many_hadamard, many_hadamard, oracle, qubits, num_iter) return amp_prog
def _create_bv_circuit(self, bit_map): """ Implementation of the Bernstein-Vazirani Algorithm. Given a list of input qubits and an ancilla bit, all initially in the :math:`\\vert 0\\rangle` state, create a program that can find :math:`\\vec{a}` with one query to the given oracle. :param Dict[String, String] bit_map: truth-table of a function for Bernstein-Vazirani with the keys being all possible bit vectors strings and the values being the function values :rtype: Program """ unitary, _ = self._compute_unitary_oracle_matrix(bit_map) full_bv_circuit = pq.Program() full_bv_circuit.defgate("BV-ORACLE", unitary) # Put ancilla bit into minus state full_bv_circuit.inst(X(self.ancilla), H(self.ancilla)) full_bv_circuit.inst([H(i) for i in self.computational_qubits]) full_bv_circuit.inst( tuple(["BV-ORACLE"] + sorted( self.computational_qubits + [self.ancilla], reverse=True))) full_bv_circuit.inst([H(i) for i in self.computational_qubits]) return full_bv_circuit
def decomposed_diffusion_program(qubits): """ Constructs the diffusion operator used in Grover's Algorithm, acted on both sides by an a Hadamard gate on each qubit. Note that this means that the matrix representation of this operator is diag(1, -1, ..., -1). In particular, this decomposes the diffusion operator, which is a :math:`2**{len(qubits)}\times2**{len(qubits)}` sparse matrix, into :math:`\mathcal{O}(len(qubits)**2) single and two qubit gates. See C. Lavor, L.R.U. Manssur, and R. Portugal (2003) `Grover's Algorithm: Quantum Database Search`_ for more information. .. _`Grover's Algorithm: Quantum Database Search`: https://arxiv.org/abs/quant-ph/0301079 :param qubits: A list of ints corresponding to the qubits to operate on. The operator operates on bistrings of the form ``|qubits[0], ..., qubits[-1]>``. """ program = pq.Program() if len(qubits) == 1: program.inst(Z(qubits[0])) else: program.inst([X(q) for q in qubits]) program.inst(H(qubits[-1])) program.inst(RZ(-np.pi, qubits[0])) program += (ControlledProgramBuilder().with_controls( qubits[:-1]).with_target(qubits[-1]).with_operation( X_GATE).with_gate_name(X_GATE_LABEL).build()) program.inst(RZ(-np.pi, qubits[0])) program.inst(H(qubits[-1])) program.inst([X(q) for q in qubits]) return program
def diffusion_operator(qubits): """Constructs the (Grover) diffusion operator on qubits, assuming they are ordered from most significant qubit to least significant qubit. The diffusion operator is the diagonal operator given by(1, -1, -1, ..., -1). :param qubits: A list of ints corresponding to the qubits to operate on. The operator operates on bistrings of the form |qubits[0], ..., qubits[-1]>. """ p = pq.Program() if len(qubits) == 1: p.inst(H(qubits[0])) p.inst(Z(qubits[0])) p.inst(H(qubits[0])) else: p.inst(map(X, qubits)) p.inst(H(qubits[-1])) p.inst(RZ(-np.pi)(qubits[0])) p += n_qubit_control(qubits[:-1], qubits[-1], np.array([[0, 1], [1, 0]]), "NOT") p.inst(RZ(-np.pi)(qubits[0])) p.inst(H(qubits[-1])) p.inst(map(X, qubits)) return p
def oracle_function(unitary_funct, qubits, ancilla): """ Defines an oracle that performs the following unitary transformation: |x>|y> -> |x>|f(x) xor y> Allocates one scratch bit. :param np.array unitary_funct: Matrix representation of the function f, i.e. the unitary transformation that must be applied to a state |x> to put f(x) in qubit 0, where f(x) returns either 0 or 1 for any n-bit string x :param np.array qubits: List of qubits that enter as input |x>. :param Qubit ancilla: Qubit to serve as input |y>. :return: A program that performs the above unitary transformation. :rtype: Program """ if not is_unitary(unitary_funct): raise ValueError("Function must be unitary.") p = pq.Program() scratch_bit = p.alloc() bits_for_funct = [scratch_bit] + qubits p.defgate("FUNCT", unitary_funct) p.defgate("FUNCT-INV", unitary_funct.T.conj()) # TODO Remove the cast to tuple once this is supported. p.inst(tuple(['FUNCT'] + bits_for_funct)) p.inst(CNOT(qubits[0], ancilla)) p.inst(tuple(['FUNCT-INV'] + bits_for_funct)) p.free(scratch_bit) return p
def bernstein_vazirani(oracle, qubits, ancilla): """ Implementation of the Bernstein-Vazirani Algorithm. Given a list of input qubits and an ancilla bit, all initially in the :math:`\\vert 0\\rangle` state, create a program that can find :math:`\\vec{a}` with one query to the given oracle. :param Program oracle: Program representing unitary application of function :param list(int) qubits: List of qubits that enter as state :math:`\\vert x\\rangle`. :param int ancilla: Ancillary qubit to serve as input :math:`\\vert y\\rangle`, where the answer will be written to. :return: A program corresponding to the desired instance of the Bernstein-Vazirani Algorithm. :rtype: Program """ p = pq.Program() # Put ancilla bit into minus state p.inst(X(ancilla), H(ancilla)) # Apply Hadamard, Unitary function, and Hadamard again p.inst(list(map(H, qubits))) p += oracle p.inst(list(map(H, qubits))) return p
def test_gradient_program(): f_h = 0.25 precision = 2 trial_prog = gradient_program(f_h, precision) result_prog = pq.Program([H(0), H(1)]) phase_factor = np.exp(-1.0j * 2 * np.pi * abs(f_h)) U = np.array([[phase_factor, 0], [0, phase_factor]]) q_out = range(precision, precision + 1) for i in range(precision): if i > 0: U = np.dot(U, U) cU = controlled(U) name = "CONTROLLED-U{0}".format(2**i) result_prog.defgate(name, cU) result_prog.inst((name, i) + tuple(q_out)) result_prog.inst([ H(1), CPHASE(1.5707963267948966, 0, 1), H(0), SWAP(0, 1), MEASURE(0, [0]), MEASURE(1, [1]) ]) assert (trial_prog == result_prog)
def expectation(self, prep_prog, operator_programs=None): """ Calculate the expectation value of operators given a state prepared by prep_program. :param Program prep_prog: Quil program for state preparation. :param list operators: A list of PauliTerms. Default is Identity operator. :returns: Expectation value of the operators. :rtype: float """ if operator_programs is None: operator_programs = [pq.Program()] if not isinstance(prep_prog, pq.Program): raise TypeError("prep_prog variable must be a Quil program object") payload = { 'type': TYPE_EXPECTATION, 'state-preparation': prep_prog.out(), 'operators': [x.out() for x in operator_programs] } add_rng_seed_to_payload(payload, self.random_seed) res = self.post_job(payload, headers=self.json_headers) return self.process_response(res)
def diffusion_program(qubits): """Constructs the diffusion operator used in Grover's Algorithm, acted on both sides by an a Hadamard gate on each qubit. Note that this means that the matrix representation of this operator is diag(1, -1, ..., -1). See C. Lavor, L.R.U. Manssur, and R. Portugal (2003) `Grover's Algorithm: Quantum Database Search`_ for more information. .. _`Grover's Algorithm: Quantum Database Search`: https://arxiv.org/abs/quant-ph/0301079 :param qubits: A list of ints corresponding to the qubits to operate on. The operator operates on bistrings of the form |qubits[0], ..., qubits[-1]>. """ diffusion_program = pq.Program() if len(qubits) == 1: diffusion_program.inst(Z(qubits[0])) else: diffusion_program.inst([X(q) for q in qubits]) diffusion_program.inst(H(qubits[-1])) diffusion_program.inst(RZ(-np.pi)(qubits[0])) diffusion_program += (ControlledProgramBuilder() .with_controls(qubits[:-1]) .with_target(qubits[-1]) .with_operation(X_GATE) .with_gate_name("NOT").build()) diffusion_program.inst(RZ(-np.pi)(qubits[0])) diffusion_program.inst(H(qubits[-1])) diffusion_program.inst([X(q) for q in qubits]) return diffusion_program
def psi_ref(params): """Construct a Quil program for the vector (beta, gamma). :param params: array of 3 . p angles, betas first, then gammas, then omega :return: a pyquil program object """ if len(params) != 3 * self.steps: raise ValueError( """params doesn't match the number of parameters set by `steps`""") betas = params[:self.steps] gammas = params[self.steps:2 * self.steps] omegas = params[2 * self.steps:] prog = pq.Program() prog += self.ref_state_prep for idx in range(self.steps): for fprog in constraint_para_programs[idx]: prog += fprog(gammas[idx]) for fprog in localfield_para_programs[idx]: prog += fprog(omegas[idx]) for fprog in driver_para_programs[idx]: prog += fprog(betas[idx]) return prog