def run_grover(eng, n, Ox): # start in uniform superposition x = eng.allocate_qureg(n) All(H) | x # the auxiliary indicator qbit, prepare it as '|->' y = eng.allocate_qubit() X | y H | y # number of iterations we have to run: ITER = int(math.pi / 4.0 * math.sqrt(1 << n)) with Loop(eng, ITER): # oracle adds a (-1)-phase to the solution(s) Ox(eng, x, y) # reflection across uniform superposition with Compute(eng): All(H) | x All(X) | x with Control(eng, x[:-1]): # Z == H-CNOT(x[-1])-H ?? Z | x[-1] Uncompute(eng) All(Measure) | x Measure | y eng.flush() return ''.join([str(int(q)) for q in x])
def add_constant_modN(eng, constant, N, quint): # pylint: disable=invalid-name """ Add a classical constant c to a quantum integer (qureg) quint modulo N using Draper addition. This function uses Draper addition and the construction from https://arxiv.org/abs/quant-ph/0205095. """ if constant < 0 or constant > N: raise ValueError('Pre-condition failed: 0 <= constant < N') AddConstant(constant) | quint with Compute(eng): SubConstant(N) | quint ancilla = eng.allocate_qubit() CNOT | (quint[-1], ancilla) with Control(eng, ancilla): AddConstant(N) | quint SubConstant(constant) | quint with CustomUncompute(eng): X | quint[-1] CNOT | (quint[-1], ancilla) X | quint[-1] del ancilla AddConstant(constant) | quint
def lcu_oaa(eng, list_of_unitaries, coefts, ctrl, sys, ctrl_dim, sys_dim, rounds=1): phi = -1 * math.pi for i in range(0, rounds): cond_phase(eng, ctrl, sys, phi) with Dagger(eng): lcu_basic(eng, list_of_unitaries, coefts, ctrl, sys, ctrl_dim, sys_dim) size = pow(2, ctrl_dim) for l in range(1, size): # -R flips sign of everything except 00..0 temp = np.binary_repr(i) temp = temp.zfill(ctrl_dim) # pad with zeros for fixed length bin with Compute(eng): for j in range(0, ctrl_dim): if (int(temp[j]) == 0): X | ctrl[j] with Control(eng, ctrl): Ph(phi) | sys[0] # flip sign using any one sys qubit Uncompute(eng) lcu_basic(eng, list_of_unitaries, coefts, ctrl, sys, ctrl_dim, sys_dim) print("Amplitudes of ctrl+sys state after {} rounds of OAA:\n".format( int(i) + 1)) print_amplitudes(eng, ctrl + sys, ctrl_dim + sys_dim)
def lcu_controlled_unitary(eng, list_of_U, coefts, ctrl, sys, sys_dim): size = len(list_of_U) if not size: print('Error in lcu - I got an empty list of unitaries!') ctrl_dim = math.ceil(math.log(size, 2)) for i in range(0, size): temp = np.binary_repr(i) temp = temp.zfill(ctrl_dim) # pad with zeros for fixed length bin with Compute(eng): for j in range(0, ctrl_dim): if (int(temp[j]) == 0): X | ctrl[j] # if unitaries passed are qubitoperator, directly apply them, no unpacking required if isinstance(list_of_U[0], QubitOperator): with Control(eng, ctrl): list_of_U[i] | sys Uncompute(eng) else: with Control(eng, ctrl): if (isinstance(coefts[i], complex)): # can be i, or -1 Ph(0.5 * math.pi) | sys[j] # apply global phase i if (np.sign(-1j * coefts[i]) < 0): Ph(math.pi) | sys[j] # global phase is actually -i elif (np.sign(coefts[i]) < 0): Ph(math.pi) | sys[j] # apply global phase -1 for j in range(0, sys_dim): if (list_of_U[i][j] == I): continue list_of_U[i][j] | sys[j] Uncompute(eng)
def ExecuteGrover(engine, n, oracle): x = engine.allocate_qureg(n) All(H) | x num_it = int(math.pi/4.*math.sqrt(1 << n)) oracle_out = engine.allocate_qubit() X | oracle_out H | oracle_out with Loop(engine, num_it): oracle(engine, x, oracle_out) with Compute(engine): All(H) | x All(X) | x with Control(engine, x[0:-1]): Z | x[-1] Uncompute(engine) All(Measure) | x Measure | oracle_out engine.flush() return [int(qubit) for qubit in x]
def add_constant_modN(eng, c, N, quint): """ Adds a classical constant c to a quantum integer (qureg) quint modulo N using Draper addition and the construction from https://arxiv.org/abs/quant-ph/0205095. """ assert (c < N and c >= 0) AddConstant(c) | quint with Compute(eng): SubConstant(N) | quint ancilla = eng.allocate_qubit() CNOT | (quint[-1], ancilla) with Control(eng, ancilla): AddConstant(N) | quint SubConstant(c) | quint with CustomUncompute(eng): X | quint[-1] CNOT | (quint[-1], ancilla) X | quint[-1] del ancilla AddConstant(c) | quint
def _decompose_CnU(cmd): # pylint: disable=invalid-name """ Decompose a multi-controlled gate U with n control qubits into a single- controlled U. It uses (n-1) work qubits and 2 * (n-1) Toffoli gates for general U and (n-2) work qubits and 2n - 3 Toffoli gates if U is an X-gate. """ eng = cmd.engine qubits = cmd.qubits ctrl_qureg = cmd.control_qubits gate = cmd.gate n_controls = get_control_count(cmd) # specialized for X-gate if gate == XGate() and n_controls > 2: n_controls -= 1 ancilla_qureg = eng.allocate_qureg(n_controls - 1) with Compute(eng): Toffoli | (ctrl_qureg[0], ctrl_qureg[1], ancilla_qureg[0]) for ctrl_index in range(2, n_controls): Toffoli | ( ctrl_qureg[ctrl_index], ancilla_qureg[ctrl_index - 2], ancilla_qureg[ctrl_index - 1], ) ctrls = [ancilla_qureg[-1]] # specialized for X-gate if gate == XGate() and get_control_count(cmd) > 2: ctrls += [ctrl_qureg[-1]] with Control(eng, ctrls): gate | qubits Uncompute(eng)
def test_quantummultiplication(eng): qureg_a = eng.allocate_qureg(3) qureg_b = eng.allocate_qureg(3) qureg_c = eng.allocate_qureg(7) init(eng, qureg_a, 7) init(eng, qureg_b, 3) MultiplyQuantum | (qureg_a, qureg_b, qureg_c) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 1], qureg_a)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0], qureg_b)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 0, 1, 0, 1, 0, 0], qureg_c)) All(Measure) | qureg_a All(Measure) | qureg_b All(Measure) | qureg_c init(eng, qureg_a, 7) init(eng, qureg_b, 3) init(eng, qureg_c, 21) assert 1.0 == pytest.approx(eng.backend.get_probability([0, 0, 0, 0, 0, 0, 0], qureg_c)) init(eng, qureg_a, 2) init(eng, qureg_b, 3) with Compute(eng): MultiplyQuantum | (qureg_a, qureg_b, qureg_c) Uncompute(eng) assert 1.0 == pytest.approx(eng.backend.get_probability([0, 1, 0], qureg_a)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0], qureg_b)) assert 1.0 == pytest.approx(eng.backend.get_probability([0, 0, 0, 0, 0, 0, 0], qureg_c))
def test_quantumdivision(eng): qureg_a = eng.allocate_qureg(4) qureg_b = eng.allocate_qureg(4) qureg_c = eng.allocate_qureg(4) init(eng, qureg_a, 10) init(eng, qureg_c, 3) DivideQuantum | (qureg_a, qureg_b, qureg_c) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 0, 0, 0], qureg_a)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0, 0], qureg_b)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0, 0], qureg_c)) All(Measure) | qureg_a All(Measure) | qureg_b All(Measure) | qureg_c init(eng, qureg_a, 1) # reset init(eng, qureg_b, 3) # reset init(eng, qureg_a, 11) with Compute(eng): DivideQuantum | (qureg_a, qureg_b, qureg_c) Uncompute(eng) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0, 1], qureg_a)) assert 1.0 == pytest.approx(eng.backend.get_probability([0, 0, 0, 0], qureg_b)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 0, 0], qureg_c)) All(Measure) | qureg_a All(Measure) | qureg_b All(Measure) | qureg_c
def _decompose_QAA(cmd): """ Decompose the Quantum Amplitude Apmplification algorithm as a gate. """ eng = cmd.engine # System-qubit is the first qubit/qureg. Ancilla qubit is the second qubit system_qubits = cmd.qubits[0] qaa_ancilla = cmd.qubits[1] # The Oracle and the Algorithm Oracle = cmd.gate.oracle A = cmd.gate.algorithm # Apply the oracle to invert the amplitude of the good states, S_Chi Oracle(eng, system_qubits, qaa_ancilla) # Apply the inversion of the Algorithm, # the inversion of the aplitude of |0> and the Algorithm with Compute(eng): with Dagger(eng): A(eng, system_qubits) All(X) | system_qubits with Control(eng, system_qubits[0:-1]): Z | system_qubits[-1] with CustomUncompute(eng): All(X) | system_qubits A(eng, system_qubits) Ph(math.pi) | system_qubits[0]
def test_control(): backend = DummyEngine(save_commands=True) eng = MainEngine(backend=backend, engine_list=[DummyEngine()]) qureg = eng.allocate_qureg(2) with _control.Control(eng, qureg): qubit = eng.allocate_qubit() with Compute(eng): Rx(0.5) | qubit H | qubit Uncompute(eng) with _control.Control(eng, qureg[0]): H | qubit eng.flush() assert len(backend.received_commands) == 8 assert len(backend.received_commands[0].control_qubits) == 0 assert len(backend.received_commands[1].control_qubits) == 0 assert len(backend.received_commands[2].control_qubits) == 0 assert len(backend.received_commands[3].control_qubits) == 0 assert len(backend.received_commands[4].control_qubits) == 2 assert len(backend.received_commands[5].control_qubits) == 0 assert len(backend.received_commands[6].control_qubits) == 1 assert len(backend.received_commands[7].control_qubits) == 0 assert backend.received_commands[4].control_qubits[0].id == qureg[0].id assert backend.received_commands[4].control_qubits[1].id == qureg[1].id assert backend.received_commands[6].control_qubits[0].id == qureg[0].id
def _decompose_time_evolution_individual_terms(cmd): """ Implements a TimeEvolution gate with a hamiltonian having only one term. To implement exp(-i * t * hamiltonian), where the hamiltonian is only one term, e.g., hamiltonian = X0 x Y1 X Z2, we first perform local transformations to in order that all Pauli operators in the hamiltonian are Z. We then implement exp(-i * t * (Z1 x Z2 x Z3) and transform the basis back to the original. For more details see, e.g., James D. Whitfield, Jacob Biamonte & Aspuru-Guzik Simulation of electronic structure Hamiltonians using quantum computers, Molecular Physics, 109:5, 735-750 (2011). or Nielsen and Chuang, Quantum Computation and Information. """ assert len(cmd.qubits) == 1 qureg = cmd.qubits[0] eng = cmd.engine time = cmd.gate.time hamiltonian = cmd.gate.hamiltonian assert len(hamiltonian.terms) == 1 term = list(hamiltonian.terms)[0] coefficient = hamiltonian.terms[term] check_indices = set() # Check that hamiltonian is not identity term, # Previous __or__ operator should have apply a global phase instead: assert not term == () # hamiltonian has only a single local operator if len(term) == 1: with Control(eng, cmd.control_qubits): if term[0][1] == 'X': Rx(time * coefficient * 2.) | qureg[term[0][0]] elif term[0][1] == 'Y': Ry(time * coefficient * 2.) | qureg[term[0][0]] else: Rz(time * coefficient * 2.) | qureg[term[0][0]] # hamiltonian has more than one local operator else: with Control(eng, cmd.control_qubits): with Compute(eng): # Apply local basis rotations for index, action in term: check_indices.add(index) if action == 'X': H | qureg[index] elif action == 'Y': Rx(math.pi / 2.) | qureg[index] # Check that qureg had exactly as many qubits as indices: assert check_indices == set((range(len(qureg)))) # Compute parity for i in range(len(qureg) - 1): CNOT | (qureg[i], qureg[i + 1]) Rz(time * coefficient * 2.) | qureg[-1] # Uncompute parity and basis change Uncompute(eng)
def oraculo(eng, x, ctrl): with Compute(eng): All(NOT) | x[1::2] with Control(eng, x): NOT | ctrl Uncompute(eng) return
def simple_oracle(eng, system_q, control): # This oracle selects the state |1010101> as the one marked with Compute(eng): All(X) | system_q[1::2] with Control(eng, system_q): X | control Uncompute(eng)
def test_inverse_addition_with_control_carry(eng): qunum_a = eng.allocate_qureg(4) qunum_b = eng.allocate_qureg(4) control_bit = eng.allocate_qubit() qunum_c = eng.allocate_qureg(2) X | qunum_a[1] All(X) | qunum_b[0:4] X | control_bit with Compute(eng): with Control(eng, control_bit): AddQuantum | (qunum_a, qunum_b, qunum_c) Uncompute(eng) eng.flush() assert 1.0 == pytest.approx(eng.backend.get_probability([0, 1, 0, 0], qunum_a)) assert 1.0 == pytest.approx(eng.backend.get_probability([1, 1, 1, 1], qunum_b)) assert 1.0 == pytest.approx(eng.backend.get_probability([1], control_bit)) assert 1.0 == pytest.approx(eng.backend.get_probability([0, 0], qunum_c)) All(Measure) | qunum_a All(Measure) | qunum_b Measure | control_bit All(Measure) | qunum_c
def _decompose_ccy(cmd): """ Decompose CNOT gates. """ ctrl = cmd.control_qubits eng = cmd.engine with Compute(eng): S | cmd.qubits[0] Toffoli | (ctrl[0], ctrl[1], cmd.qubits[0][0]) Uncompute(eng)
def _decompose_cz(cmd): """ Decompose CNOT gates. """ ctrl = cmd.control_qubits eng = cmd.engine with Compute(eng): H | cmd.qubits[0] CNOT | (ctrl[0], cmd.qubits[0][0]) Uncompute(eng)
def qnn(eng): with Compute(eng): qbn(eng) oracle(eng) Uncompute(eng)
def _decompose_swap(cmd): """ Decompose (controlled) swap gates. """ ctrl = cmd.control_qubits eng = cmd.engine with Compute(eng): CNOT | (cmd.qubits[0], cmd.qubits[1]) with Control(eng, ctrl): CNOT | (cmd.qubits[1], cmd.qubits[0]) Uncompute(eng)
def _decompose_ucr(cmd, gate_class): """ Decomposition for an uniformly controlled single qubit rotation gate. Follows decomposition in arXiv:quant-ph/0407010 section II and arXiv:quant-ph/0410066v2 Fig. 9a. For Ry and Rz it uses 2**len(ucontrol_qubits) CNOT and also 2**len(ucontrol_qubits) single qubit rotations. Args: cmd: CommandObject to decompose. gate_class: Ry or Rz """ eng = cmd.engine with Control(eng, cmd.control_qubits): if not (len(cmd.qubits) == 2 and len(cmd.qubits[1]) == 1): raise TypeError("Wrong number of qubits ") ucontrol_qubits = cmd.qubits[0] target_qubit = cmd.qubits[1] if not len(cmd.gate.angles) == 2**len(ucontrol_qubits): raise ValueError("Wrong len(angles).") if len(ucontrol_qubits) == 0: gate_class(cmd.gate.angles[0]) | target_qubit return angles1 = [] angles2 = [] for lower_bits in range(2**(len(ucontrol_qubits) - 1)): leading_0 = cmd.gate.angles[lower_bits] leading_1 = cmd.gate.angles[lower_bits + 2**(len(ucontrol_qubits) - 1)] angles1.append((leading_0 + leading_1) / 2.0) angles2.append((leading_0 - leading_1) / 2.0) rightmost_cnot = {} for i in range(len(ucontrol_qubits) + 1): rightmost_cnot[i] = True _apply_ucr_n( angles=angles1, ucontrol_qubits=ucontrol_qubits[:-1], target_qubit=target_qubit, eng=eng, gate_class=gate_class, rightmost_cnot=rightmost_cnot, ) # Very custom usage of Compute/CustomUncompute in the following. with Compute(cmd.engine): CNOT | (ucontrol_qubits[-1], target_qubit) _apply_ucr_n( angles=angles2, ucontrol_qubits=ucontrol_qubits[:-1], target_qubit=target_qubit, eng=eng, gate_class=gate_class, rightmost_cnot=rightmost_cnot, ) with CustomUncompute(eng): CNOT | (ucontrol_qubits[-1], target_qubit)
def Quantum_Adder(state_a, state_b, eng=engine): ''' parameters state_a:(qureg) state_b:(qureg) ''' with Compute(eng): quantum_fourier_transform(state_a) ancillary_add(state_a, state_b) Uncompute(eng)
def _decompose_rx(cmd): """Decompose the Rx gate.""" qubit = cmd.qubits[0] eng = cmd.engine angle = cmd.gate.angle with Control(eng, cmd.control_qubits): with Compute(eng): H | qubit Rz(angle) | qubit Uncompute(eng)
def add_minus_sign(eng): with Compute(eng): quanutm_phase_estimation(eng) X | phase_reg[2] ControlledGate( NOT, 3) | (phase_reg[0], phase_reg[1], phase_reg[2], ancilla_qubit) X | phase_reg[2] Uncompute(eng)
def _decompose_ry(cmd): """ Decompose the Ry gate.""" qubit = cmd.qubits[0] eng = cmd.engine angle = cmd.gate.angle with Control(eng, cmd.control_qubits): with Compute(eng): Rx(math.pi / 2.) | qubit Rz(angle) | qubit Uncompute(eng)
def diffusion(eng): with Compute(eng): All(H) | layer1_weight_reg All(X) | layer1_weight_reg ControlledGate(Z, 2) | (layer1_weight_reg[0], layer1_weight_reg[1], layer1_weight_reg[2]) Uncompute(eng)
def test_constant_addition(eng): qunum_a = eng.allocate_qureg(5) X | qunum_a[2] with Compute(eng): AddConstant(5) | (qunum_a) Uncompute(eng) eng.flush() assert 1.0 == pytest.approx(eng.backend.get_probability([0, 0, 1, 0, 0], qunum_a))
def _decompose_rz2rx_P(cmd): # pylint: disable=invalid-name """Decompose the Rz using negative angle.""" # Labelled 'P' for 'plus' because decomposition ends with a Ry(+pi/2) qubit = cmd.qubits[0] eng = cmd.engine angle = cmd.gate.angle with Control(eng, cmd.control_qubits): with Compute(eng): Ry(-math.pi / 2.0) | qubit Rx(-angle) | qubit Uncompute(eng)
def complex_oracle(eng, system_q, control): # This oracle selects the subspace |000000>+|111111> as the good one with Compute(eng): with Control(eng, system_q[0]): All(X) | system_q[1:] H | system_q[0] All(X) | system_q with Control(eng, system_q): X | control Uncompute(eng)
def _decompose_rz2rx_M(cmd): """ Decompose the Rz using positive angle. """ # Labelled 'M' for 'minus' because decomposition ends with a Ry(-pi/2) qubit = cmd.qubits[0] eng = cmd.engine angle = cmd.gate.angle with Control(eng, cmd.control_qubits): with Compute(eng): Ry(math.pi / 2.) | qubit Rx(angle) | qubit Uncompute(eng)
def run_grover(eng, n, oracle): """ Runs Grover's algorithm on n qubit using the provided quantum oracle. Args: eng (MainEngine): Main compiler engine to run Grover on. n (int): Number of bits in the solution. oracle (function): Function accepting the engine, an n-qubit register, and an output qubit which is flipped by the oracle for the correct bit string. Returns: solution (list<int>): Solution bit-string. """ x = eng.allocate_qureg(n) # start in uniform superposition All(H) | x # number of iterations we have to run: num_it = int(math.pi / 4. * math.sqrt(1 << n)) print("Number of iterations we have to run: {}".format(num_it)) # prepare the oracle output qubit (the one that is flipped to indicate the # solution. start in state 1/sqrt(2) * (|0> - |1>) s.t. a bit-flip turns # into a (-1)-phase. oracle_out = eng.allocate_qubit() X | oracle_out H | oracle_out # run num_it iterations with Loop(eng, num_it): # oracle adds a (-1)-phase to the solution oracle(eng, x, oracle_out) # reflection across uniform superposition with Compute(eng): All(H) | x All(X) | x with Control(eng, x[0:-1]): Z | x[-1] Uncompute(eng) All(Ph(math.pi / n)) | x All(Measure) | x Measure | oracle_out eng.flush() # return result return [int(qubit) for qubit in x]