def test_expected_tape(self): """Tests if QuantumPhaseEstimation populates the tape as expected for a fixed example""" m = qml.RX(0.3, wires=0).matrix op = qml.QuantumPhaseEstimation(m, target_wires=[0], estimation_wires=[1, 2]) tape = op.expand() with qml.tape.QuantumTape() as tape2: qml.Hadamard(1), qml.ControlledQubitUnitary(m @ m, control_wires=[1], wires=[0]), qml.Hadamard(2), qml.ControlledQubitUnitary(m, control_wires=[2], wires=[0]), qml.QFT(wires=[1, 2]).inv() assert len(tape2.queue) == len(tape.queue) assert all([ op1.name == op2.name for op1, op2 in zip(tape.queue, tape2.queue) ]) assert all([ op1.wires == op2.wires for op1, op2 in zip(tape.queue, tape2.queue) ]) assert np.allclose(tape.queue[1].matrix, tape2.queue[1].matrix) assert np.allclose(tape.queue[3].matrix, tape2.queue[3].matrix)
def test_id(self): """Tests that the id attribute can be set.""" template = qml.QuantumPhaseEstimation(np.eye(2), target_wires=[0, 1], estimation_wires=[2, 3], id="a") assert template.id == "a"
def test_same_wires(self): """Tests if a QuantumFunctionError is raised if target_wires and estimation_wires contain a common element""" with pytest.raises(qml.QuantumFunctionError, match="The target wires and estimation wires"): qml.QuantumPhaseEstimation(np.eye(2), target_wires=[0, 1], estimation_wires=[1, 2])
def test_phase_estimated_two_qubit(self): """Tests that the QPE circuit can correctly estimate the phase of a random two-qubit unitary.""" unitary = unitary_group.rvs(4, random_state=1967) eigvals, eigvecs = np.linalg.eig(unitary) state = eigvecs[:, 0] eigval = eigvals[0] phase = np.real_if_close(np.log(eigval) / (2 * np.pi * 1j)) estimates = [] wire_range = range(3, 11) for wires in wire_range: dev = qml.device("default.qubit", wires=wires) target_wires = [0, 1] estimation_wires = range(2, wires) with qml.tape.QuantumTape() as tape: # We want to prepare an eigenstate of RX, in this case |+> qml.QubitStateVector(state, wires=target_wires) qml.QuantumPhaseEstimation(unitary, target_wires=target_wires, estimation_wires=estimation_wires) qml.probs(estimation_wires) tape = tape.expand(depth=2, stop_at=lambda obj: obj.name in dev.operations) res = tape.execute(dev).flatten() if phase < 0: estimate = np.argmax(res) / 2**(wires - 2) - 1 else: estimate = np.argmax(res) / 2**(wires - 2) estimates.append(estimate) # Check that the error is monotonically decreasing for i in range(len(estimates) - 1): err1 = np.abs(estimates[i] - phase) err2 = np.abs(estimates[i + 1] - phase) assert err1 >= err2 # This is quite a large error, but we'd need to push the qubit number up more to get it # lower assert np.allclose(estimates[-1], phase, rtol=1e-2)
def qpe_circuit(): qml.Hadamard(wires=0) qml.PauliX(wires=1) qml.QuantumPhaseEstimation( qml.PauliX.matrix, target_wires=[0], estimation_wires=[1, 2], ) qml.adjoint(qml.QuantumPhaseEstimation)( qml.PauliX.matrix, target_wires=[0], estimation_wires=[1, 2], ) qml.Hadamard(wires=0) qml.PauliX(wires=1) return qml.state()
def test_expected_circuit(self): """Test if the circuit applied when using the QMC template is the same as the expected circuit for a fixed example""" p = np.ones(4) / 4 target_wires, estimation_wires = Wires(range(3)), Wires(range(3, 5)) op = QuantumMonteCarlo(p, self.func, target_wires, estimation_wires) tape = op.expand() # Do expansion in two steps to avoid also decomposing the first QubitUnitary queue_before_qpe = tape.operations[:2] # 2-qubit decomposition has 10 operations, and after is a 3-qubit gate so start at 11 queue_after_qpe = tape.expand().operations[11:] A = probs_to_unitary(p) R = func_to_unitary(self.func, 4) assert len(queue_before_qpe) == 2 assert queue_before_qpe[0].name == "QubitUnitary" assert queue_before_qpe[1].name == "QubitUnitary" assert np.allclose(queue_before_qpe[0].matrix, A) assert np.allclose(queue_before_qpe[1].matrix, R) assert queue_before_qpe[0].wires == target_wires[:-1] assert queue_before_qpe[1].wires == target_wires Q = make_Q(A, R) with qml.tape.QuantumTape() as qpe_tape: qml.QuantumPhaseEstimation(Q, target_wires, estimation_wires) qpe_tape = qpe_tape.expand() assert len(queue_after_qpe) == len(qpe_tape.operations) assert all(o1.name == o2.name for o1, o2 in zip(queue_after_qpe, qpe_tape.operations)) assert all( np.allclose(o1.matrix, o2.matrix) for o1, o2 in zip(queue_after_qpe, qpe_tape.operations)) assert all(o1.wires == o2.wires for o1, o2 in zip(queue_after_qpe, qpe_tape.operations))
def test_phase_estimated(self, phase): """Tests that the QPE circuit can correctly estimate the phase of a simple RX rotation.""" estimates = [] wire_range = range(2, 10) for wires in wire_range: dev = qml.device("default.qubit", wires=wires) m = qml.RX(phase, wires=0).matrix target_wires = [0] estimation_wires = range(1, wires) with qml.tape.QuantumTape() as tape: # We want to prepare an eigenstate of RX, in this case |+> qml.Hadamard(wires=target_wires) qml.QuantumPhaseEstimation(m, target_wires=target_wires, estimation_wires=estimation_wires) qml.probs(estimation_wires) tape = tape.expand(depth=2, stop_at=lambda obj: obj.name in dev.operations) res = tape.execute(dev).flatten() initial_estimate = np.argmax(res) / 2**(wires - 1) # We need to rescale because RX is exp(- i theta X / 2) and we expect a unitary of the # form exp(2 pi i theta X) rescaled_estimate = (1 - initial_estimate) * np.pi * 4 estimates.append(rescaled_estimate) # Check that the error is monotonically decreasing for i in range(len(estimates) - 1): err1 = np.abs(estimates[i] - phase) err2 = np.abs(estimates[i + 1] - phase) assert err1 >= err2 # This is quite a large error, but we'd need to push the qubit number up more to get it # lower assert np.allclose(estimates[-1], phase, rtol=1e-2)
def test_nested_tapes(self): with qml.tape.QuantumTape() as tape: with qml.tape.QuantumTape(): qml.PauliX(0) qml.CNOT(wires=[0, 2]) with qml.tape.QuantumTape(): qml.QuantumPhaseEstimation( qml.PauliY.matrix, target_wires=[1], estimation_wires=[2] ) qml.CNOT(wires=[1, 2]) qml.Hadamard(1) with qml.tape.QuantumTape(): qml.SWAP(wires=[0, 1]) qml.state() expected = ( " 0: ──╭QuantumTape:T0─────╭QuantumTape:T1──╭┤ State \n" + " 1: ──├QuantumTape:T0──H──╰QuantumTape:T1──├┤ State \n" + " 2: ──╰QuantumTape:T0──────────────────────╰┤ State \n" + "T0 =\n" + " 0: ──X──╭C───────────────────┤ \n" + " 2: ─────╰X──╭QuantumTape:T2──┤ \n" + " 1: ─────────╰QuantumTape:T2──┤ \n" + "T2 =\n" + " 1: ──╭QuantumPhaseEstimation(M0)──╭C──┤ \n" + " 2: ──╰QuantumPhaseEstimation(M0)──╰X──┤ \n" + "M0 =\n" + "[[ 0.+0.j -0.-1.j]\n" + " [ 0.+1.j 0.+0.j]]\n" + "\n" + "\n" + "T1 =\n" + " 0: ──╭SWAP──┤ \n" + " 1: ──╰SWAP──┤ \n" + "\n" ) assert tape.draw(wire_order=qml.wires.Wires([0, 1, 2])) == expected