def test_squared_equals_I(self): for Op_type in self.squared_equals_I_list: I = qc.Identity() U = Op_type() diff = max_absolute_difference(U(U), I) self.assertLess(diff, epsilon) I = qc.Identity(2) C = qc.CNOT() self.assertLess(max_absolute_difference(C(C), I), epsilon)
def QFT(t): Op = qc.Identity(t) H = qc.Hadamard() # The R_k gate applies a 2pi/2^k phase is the qubit is set C_Rs = {} for k in range(2, t + 1, 1): R_k = np.exp(np.pi * 1j / 2**k) * qc.RotationZ(2 * np.pi / 2**k) C_Rs[k] = qc.ControlledU(R_k) # For each qubit in order, apply the Hadamard gate, and then # apply the R_2, R_3, ... conditional on the remainder of the qubits for t_i in range(t): Op = H(Op, qubit_indices=[t_i]) for k in range(2, t + 1 - t_i, 1): Op = C_Rs[k](Op, qubit_indices=[t_i + k - 1, t_i]) # We have the QFT, but with the qubits in reverse order # Swap them back Swap = qc.Swap() for i, j in zip(range(t), range(t - 1, -1, -1)): if i >= j: break Op = Swap(Op, qubit_indices=[i, j]) return Op
def test_application_to_subsystem(self): num_tests = 10 for test_i in range(num_tests): d_left = np.random.randint(2, 4) d_op = np.random.randint(2, 4) d_right = np.random.randint(2, 4) d_total = d_left + d_op + d_right M = random_unitary_operator(d_total) I1 = qc.Identity(d_left) I2 = qc.Identity(d_right) Op = random_unitary_operator(d_op) R1 = (I1 * Op * I2)(M) R2 = Op(M, qubit_indices=range(d_left, d_left + d_op)) self.assertLess(max_absolute_difference(R1, R2), epsilon)
def test_defined_operators_unitary(self): num_tests = 10 for test_i in range(num_tests): d = np.random.randint(3, 8) I = qc.Identity(d) for Op_type, op_name in zip(self.d_ops, self.d_op_names): Op = Op_type(d=d) Op_adj = get_adjoint(Op) max_diff = max_absolute_difference(Op(Op_adj), I) self.assertLess(max_diff, epsilon) max_diff = max_absolute_difference(Op_adj(Op), I) self.assertLess(max_diff, epsilon) for Op_type, op_name, d in zip(self.non_d_ops, self.non_d_op_names, self.non_d_op_dims): Op = Op_type() Op_adj = get_adjoint(Op) I = qc.Identity(d) max_diff = max_absolute_difference(Op(Op_adj), I) self.assertLess(max_diff, epsilon) max_diff = max_absolute_difference(Op_adj(Op), I) self.assertLess(max_diff, epsilon)
def test_random_operator_unitary(self): num_tests = 10 for test_i in range(num_tests): d = np.random.randint(3, 8) Op = random_unitary_operator(d) Op_adj = get_adjoint(Op) I = qc.Identity(d) max_diff = max_absolute_difference(Op(Op_adj), I) self.assertLess(max_diff, epsilon) max_diff = max_absolute_difference(Op_adj(Op), I) self.assertLess(max_diff, epsilon)
def test_operator_identity_composition(self): """ The composition of an operator with the identity operator should be the same as the original operator. """ for test_i, (d, U) in enumerate(zip(self.ds, self.U1s)): I = qc.Identity(d) R = I(U(I)) max_diff = max_absolute_difference(U, R) self.assertLess(max_diff, epsilon)
def test_U_f_unitary(self): num_tests = 10 for test_i in range(num_tests): d = np.random.randint(3, 7) f = random_boolean_function(d) Op = qc.U_f(f, d=d+1) Op_adj = get_adjoint(Op) I = qc.Identity(d+1) max_diff = max_absolute_difference(Op(Op_adj), I) self.assertLess(max_diff, epsilon) max_diff = max_absolute_difference(Op_adj(Op), I) self.assertLess(max_diff, epsilon)
def test_same_as_applying_to_substate(self): num_tests = 10 for test_i in range(num_tests): state_d = np.random.randint(2, 8) op_d = np.random.randint(1, state_d + 1) x = random_state(state_d) I = qc.Identity(state_d) U = random_unitary_operator(op_d) qubit_indices = np.random.choice(state_d, size=op_d, replace=False) R1 = U(copy.deepcopy(x), qubit_indices=qubit_indices) R2 = (U(I, qubit_indices=qubit_indices))(copy.deepcopy(x)) self.assertLess(max_absolute_difference(R1, R2), epsilon)
def test_tensor_product_operator_unitary(self): num_tests = 10 for test_i in range(num_tests): d1 = np.random.randint(3, 5) d2 = np.random.randint(3, 5) d = d1 + d2 Op1 = random_unitary_operator(d1) Op2 = random_unitary_operator(d2) Op = Op1 * Op2 Op_adj = get_adjoint(Op) I = qc.Identity(d) max_diff = max_absolute_difference(Op(Op_adj), I) self.assertLess(max_diff, epsilon) max_diff = max_absolute_difference(Op_adj(Op), I) self.assertLess(max_diff, epsilon)
def test_application_to_qubit_subset(self): """ Instead of applying the tensor product of the Identity operator with another operator to a state, we can apply the operator to a subset of axes of the state. We should get the same result. We can also permute the order of operators in the tensor product, and correspondingly permute the application order. """ num_tests = 10 I = qc.Identity() for test_i in range(num_tests): d = np.random.randint(3, 8) num_apply_to = np.random.randint(2, d) apply_to_indices = np.random.choice(d, size=num_apply_to, replace=False) M_all = None Ops = [] for qubit_i in range(d): if qubit_i in apply_to_indices: Op = random_unitary_operator(d=1) else: Op = I Ops.append(Op) if M_all is None: M_all = Op else: M_all = M_all * Op M_subset = None for apply_to_index in apply_to_indices: Op = Ops[apply_to_index] if M_subset is None: M_subset = Op else: M_subset = M_subset * Op x = random_state(d) result1 = M_all(x) result2 = M_subset(x, qubit_indices=apply_to_indices) max_diff = max_absolute_difference(result1, result2) self.assertLess(max_diff, epsilon)
def grover_algorithm(d, f): # The operators we will need Oracle = qc.U_f(f, d=d+1) H_d = qc.Hadamard(d) H = qc.Hadamard() N = 2**d zero_projector = np.zeros((N, N)) zero_projector[0, 0] = 1 Inversion = H_d((2 * qc.Operator.from_matrix(zero_projector) - qc.Identity(d))(H_d)) Grover = Inversion(Oracle, qubit_indices=range(d)) # Initial state state = qc.zeros(d) * qc.ones(1) state = (H_d * H)(state) # Number of Grover iterations angle_to_rotate = np.arccos(np.sqrt(1 / N)) rotation_angle = 2 * np.arcsin(np.sqrt(1 / N)) iterations = int(round(angle_to_rotate / rotation_angle)) for i in range(iterations): state = Grover(state) measurements = state.measure(qubit_indices=range(d)) return measurements