def hipster_multi(): """Multi-qubit, optimized application.""" nbits = 7 for bits in helper.bitprod(nbits): psi = state.bitstring(*bits) for target in range(1, nbits): # Full matrix (O(n*n). op = (ops.Identity(target - 1) * ops.Cnot(target - 1, target) * ops.Identity(nbits - target - 1)) psi1 = op(psi) # Single Qubit (O(n)) psi = apply_controlled_gate(ops.PauliX(), target - 1, target, psi) if not psi.is_close(psi1): raise AssertionError('Invalid Single Gate Application.') psi = state.bitstring(1, 1, 0, 0, 1) pn = ops.Cnot(1, 4)(psi, 1) if not pn.is_close(apply_controlled_gate(ops.PauliX(), 1, 4, psi)): raise AssertionError('Invalid Cnot') pn = ops.Cnot(4, 1)(psi, 1) if not pn.is_close(apply_controlled_gate(ops.PauliX(), 4, 1, psi)): raise AssertionError('Invalid Cnot') pn = ops.ControlledU(0, 1, ops.ControlledU(1, 4, ops.PauliX()))(psi) psi = state.qubit(alpha=0.6) * state.ones(2) pn = ops.Cnot(0, 2)(psi) if not pn.is_close(apply_controlled_gate(ops.PauliX(), 0, 2, psi)): raise AssertionError('Invalid Cnot')
def test_padding(self): ident = ops.Identity(3) h = ops.Hadamard() op = ident(h, 0) op_manual = h * ops.Identity(2) self.assertTrue(op.is_close(op_manual)) op = ident(h, 1) op_manual = ops.Identity() * h * ops.Identity() self.assertTrue(op.is_close(op_manual)) op = ident(h, 2) op_manual = ops.Identity(2) * h self.assertTrue(op.is_close(op_manual)) ident = ops.Identity(4) cx = ops.Cnot(0, 1) op = ident(cx, 0) op_manual = cx * ops.Identity(2) self.assertTrue(op.is_close(op_manual)) op = ident(cx, 1) op_manual = ops.Identity(1) * cx * ops.Identity(1) self.assertTrue(op.is_close(op_manual)) op = ident(cx, 2) op_manual = ops.Identity(2) * cx self.assertTrue(op.is_close(op_manual))
def operator_per_state(): """First foray into more effective math for 1-qubit operators.""" # Make product state q0 = state.qubit(alpha=0.5) q1 = state.qubit(alpha=0.9) psi = q0 * q1 # Compute via combined operator. op = ops.PauliX() * ops.Identity(1) psi1 = op(psi) # Combine via 1-qubit on q0 * q1 psi2 = ops.PauliX()(q0) * q1 if not psi1.is_close(psi2): raise AssertionError( 'Wrong tensor math and application of 1-qubit gate.') # Same thing, but apply to qubit 1. op = ops.Identity() * ops.PauliX() psi1 = op(psi) psi2 = q0 * ops.PauliX()(q1) if not psi1.is_close(psi2): raise AssertionError( 'Wrong tensor math and application of 1-qubit gate.')
def make_u(nbits: int, constant_c: Tuple[bool]) -> ops.Operator: """Make general Bernstein Oracle.""" # For each '1' at index i in the constant_c, build a Cnot from # bit 0 to the bottom bit. For example for string |101> # # |0> --- H --- o-------- # |0> --- H ----|-------- # |0> --- H ----|-- o --- # | | # |1> --- H --- X - X --- # op = ops.Identity(nbits) for idx in range(nbits - 1): if constant_c[idx]: op = ops.Identity(idx) * ops.Cnot(idx, nbits - 1) @ op # Note that the |+> basis, a cnot is the same as a single Z-gate. # This would also work: # op = (ops.Identity(idx) * ops.PauliZ() * # ops.Identity(nbits - 1 - idx)) @ op if not op.is_unitary(): raise AssertionError('Constructed non-unitary operator.') return op
def hipster_single(): """Single-qubit Hipster Technique.""" # This is a nice trick, outlined in this paper on "Hipster": # https://arxiv.org/pdf/1601.07195.pdf # # The observation is that to apply a single-qubit gate to a # gubit with index i, take the binary representation of inidices and # apply the transformation matrix to the elements according # to the power of 2 index. Generally: # "Performing a single-qubit gate on qubit k of n-qubit quantum # register applies G to pairs of amplitudes whose indices differ in # k-th bits of their binary index". # # For example, for a 2-qubit system, to apply a gate to qubit 0: # apply G to # q11, q12 psi[0], psi[1] # q21, q22 psi[2], psi[3] # # To apply to qubit 1: # q11, q12 psi[0], psi[2] # q21, q22 psi[1], psi[3] # # 'Outer loop' jumps by 2**(nbits+1) # 'Inner loop' jumps by 2**k # # To maintain the qubit / index ordering of this infrastructure, # the qubit index in the paper is reversed to the qubit index here. # (Hence the (nbits - qubit - 1) above) # # Make sure that for sample gates and all states the transformations # are identical. # for gate in (ops.PauliX(), ops.PauliZ(), ops.Hadamard(), ops.RotationX(0.5)): nbits = 5 for bits in helper.bitprod(nbits): psi = state.bitstring(*bits) qubit = random.randint(0, nbits - 1) # Full matrix (O(n*n). op = ops.Identity(qubit) * gate * ops.Identity(nbits - qubit - 1) psi1 = op(psi) # Single Qubit (O(n)) psi = apply_single_gate(gate, qubit, psi) if not psi.is_close(psi1): raise AssertionError('Invalid Single Gate Application.')
def random_gates(min_length, max_length, num_experiments): """Just create random sequences, find the best.""" base = [to_su2(ops.Hadamard()), to_su2(ops.Tgate())] U = (ops.RotationX(2.0 * np.pi * random.random()) @ ops.RotationY(2.0 * np.pi * random.random()) @ ops.RotationZ(2.0 * np.pi * random.random())) min_dist = 1000 for _ in range(num_experiments): seq_length = min_length + random.randint(0, max_length) U_approx = ops.Identity() for _ in range(seq_length): g = random.randint(0, 1) U_approx = U_approx @ base[g] dist = trace_dist(U, U_approx) min_dist = min(dist, min_dist) phi1 = U(state.zero) phi2 = U_approx(state.zero) print('Trace Dist: {:.4f} State: {:6.4f}%'. format(min_dist, 100.0 * (1.0 - np.real(np.dot(phi1, phi2.conj())))))
def operator_order(): """Evaluate order of operations and corresponding matmuls.""" hi = ops.Hadamard() * ops.Identity() cx = ops.Cnot(0, 1) # Make sure that the order of evaluation is correct. For example, # this simple circuit: # # |0> --- H --- o --- # | # |0> ----------X --- # # p0 p1 p2 # # Can be evaluated step wise, applying each gate to psi: psi_0 = state.zeros(2) psi_1 = hi(psi_0) psi_2 = cx(psi_1) # Or via a combined operator. Yet, the order ot the ops # has to be reversed from above picture: combined_op = (cx @ hi) combined_psi = state.zeros(2) combined_psi_2 = combined_op(combined_psi) if not psi_2.is_close(combined_psi_2): raise AssertionError('Invalid order of operators from matmul') # This can also be expressed via the function call construct: combined_f = hi(cx) combined_f_psi = state.zeros(2) combined_f_psi_2 = combined_f(combined_f_psi) if not psi_2.is_close(combined_f_psi_2): raise AssertionError('Invalid order of operators from call construct')
def with_matmul(): psi = state.zeros(n) ident = ops.Identity(n) h = ops.Hadamard(n) psi = (ident @ h)(psi) return psi
def test_cnot0(self): """Check implementation of ControlledU via Cnot0.""" # Check operator itself. x = ops.PauliX() * ops.Identity() self.assertTrue(ops.Cnot0(0, 1). is_close(x @ ops.Cnot(0, 1) @ x)) # Compute simplest case with Cnot0. psi = state.bitstring(1, 0) psi2 = ops.Cnot0(0, 1)(psi) self.assertTrue(psi.is_close(psi2)) # Compute via explicit constrution. psi2 = (x @ ops.Cnot(0, 1) @ x)(psi) self.assertTrue(psi.is_close(psi2)) # Different offsets. psi2 = ops.Cnot0(0, 1)(state.bitstring(0, 1)) self.assertTrue(psi2.is_close(state.bitstring(0, 0))) psi2 = ops.Cnot0(0, 3)(state.bitstring(0, 0, 0, 0, 1)) self.assertTrue(psi2.is_close(state.bitstring(0, 0, 0, 1, 1))) psi2 = ops.Cnot0(4, 0)(state.bitstring(1, 0, 0, 0, 0)) self.assertTrue(psi2.is_close(state.bitstring(0, 0, 0, 0, 0)))
def test_swap(self): """Test swap gate, various indices.""" swap = ops.Swap(0, 4) psi = swap(state.bitstring(1, 0, 1, 0, 0)) self.assertTrue(psi.is_close(state.bitstring(0, 0, 1, 0, 1))) swap = ops.Swap(2, 0) psi = swap(state.bitstring(1, 0, 0)) self.assertTrue(psi.is_close(state.bitstring(0, 0, 1))) op_manual = ops.Identity()**2 * swap * ops.Identity() psi = op_manual(state.bitstring(1, 1, 0, 1, 1, 0)) self.assertTrue(psi.is_close(state.bitstring(1, 1, 1, 1, 0, 0))) psi = swap(state.bitstring(1, 1, 0, 1, 1, 0), idx=2) self.assertTrue(psi.is_close(state.bitstring(1, 1, 1, 1, 0, 0)))
def by_op(): psi = state.zeros(n) ident = ops.Identity(n) h = ops.Hadamard(n) psi = ident(psi) psi = h(psi) return psi
def alice_manipulates(psi: state.State, bit0: int, bit1: int) -> state.State: """Alice encodes 2 classical bits in her 1 qubit.""" ret = ops.Identity(2)(psi) if bit0: ret = ops.PauliX()(ret) if bit1: ret = ops.PauliZ()(ret) return ret
def alice_manipulates(psi, bit0, bit1): """Alice encodes 2 classical bits in her 1 qubit.""" ret = ops.Identity(2)(psi) if bit0: ret = ops.PauliZ()(ret) if bit1: ret = ops.PauliX()(ret) return ret
def make_u(nbits, constant_c): """Make general Simon's Oracle.""" # Copy bits. op = ops.Identity(nbits*2) for idx in range(nbits): op = (ops.Identity(idx) * ops.Cnot(idx, idx+nbits) * ops.Identity(nbits - idx - 1)) @ op # Connect the xor's controlled by the msb(x). for idx in range(nbits): if constant_c[idx] == 1: op = (ops.Cnot(0, idx+nbits) * ops.Identity(nbits-idx-1)) @ op if not op.is_unitary(): raise AssertionError('Produced non-unitary Uf') return op
def graph_to_hamiltonian(n: int, nodes: List[int]) -> ops.Operator: """Compute Hamiltonian matrix from graph.""" H = np.zeros((2**n, 2**n)) for node in nodes: idx1 = node[0] idx2 = node[1] if idx1 > idx2: idx1, idx2 = idx2, idx1 op = 1.0 for _ in range(idx1): op = op * ops.Identity() op = op * (node[2] * ops.PauliZ()) for _ in range(idx1 + 1, idx2): op = op * ops.Identity() op = op * (node[2] * ops.PauliZ()) for _ in range(idx2 + 1, n): op = op * ops.Identity() H = H + op return ops.Operator(H)
def make_u(): """Make Simon's 2 (total 4) qubit Oracle.""" # We have to properly 'pad' the various gates to 4 qubits. # ident = ops.Identity() cnot0 = ops.Cnot(0, 2) * ident cnot1 = ops.Cnot(0, 3) cnot2 = ident * ops.Cnot(0, 1) * ident cnot3 = ident * ops.Cnot(0, 2) return cnot3 @ cnot2 @ cnot1 @ cnot0
def find_closest_u(gate_list, u): """Find the one gate in the list closest to u.""" # Linear search over list of gates - is _very_ slow. # This can be optimized by using kd-trees. # min_dist, min_u = 10, ops.Identity() for gate in gate_list: tr_dist = trace_dist(gate, u) if tr_dist < min_dist: min_dist, min_u = tr_dist, gate return min_u
def single_qubit(): """Compute Pauli representation of single qubit.""" for i in range(10): # First we construct a circuit with just one, very random qubit. # qc = circuit.qc('random qubit') qc.qubit(random.random()) qc.rx(0, math.pi * random.random()) qc.ry(0, math.pi * random.random()) qc.rz(0, math.pi * random.random()) # Every qubit (rho) can be put in the Pauli Representation, # which is this Sum over i from 0 to 3 inclusive, representing # the four Pauli matrices (including the Identity): # # 3 # rho = 1/2 * Sum(c_i * Pauli_i) # i=0 # # To compute the various factors c_i, we multiply the Pauli # matrices with the density matrix and take the trace. This # trace is the computed factor: # rho = qc.psi.density() i = np.trace(ops.Identity() @ rho) x = np.trace(ops.PauliX() @ rho) y = np.trace(ops.PauliY() @ rho) z = np.trace(ops.PauliZ() @ rho) # Let's verify the result and construct a density matrix # from the Pauli matrices using the computed factors: # new_rho = 0.5 * (i * ops.Identity() + x * ops.PauliX() + y * ops.PauliY() + z * ops.PauliZ()) if not np.allclose(rho, new_rho): raise AssertionError('Invalid Pauli Representation') print(f'qubit({qc.psi[0]:11.2f}, {qc.psi[1]:11.2f}) = ', end='') print(f'{i:11.2f} I + {x:11.2f} X + {y:11.2f} Y + {z:11.2f} Z')
def test_dft(self): """Build 'manually' a 3 qubit gate, Nielsen/Chuang Box 5.1.""" h = ops.Hadamard() op = ops.Identity(3) op = op(h, 0) op = op(ops.ControlledU(1, 0, ops.Rk(2)), 0) # S-gate op = op(ops.ControlledU(2, 0, ops.Rk(3)), 0) # T-gate op = op(h, 1) op = op(ops.ControlledU(1, 0, ops.Rk(2)), 1) # S-gate op = op(h, 2) op = op(ops.Swap(0, 2), 0) op3 = ops.Qft(3) self.assertTrue(op3.is_close(op))
def run_experiment(nbits, flavor): """Run full experiment for a given flavor of f().""" f = make_f(nbits - 1, flavor) u = ops.OracleUf(nbits, f) psi = (ops.Hadamard(nbits - 1)(state.zeros(nbits - 1)) * ops.Hadamard()(state.ones(1))) psi = u(psi) psi = (ops.Hadamard(nbits - 1) * ops.Identity(1))(psi) # Measure all of |0>. If all close to 1.0, f() is constant. for idx in range(nbits - 1): p0, _ = ops.Measure(psi, idx, tostate=0, collapse=False) if not math.isclose(p0, 1.0, abs_tol=1e-5): return exp_balanced return exp_constant
def test_rk(self): rk0 = ops.Rk(0) self.assertTrue(rk0.is_close(ops.Identity())) rk1 = ops.Rk(1) self.assertTrue(rk1.is_close(ops.PauliZ())) rk2 = ops.Rk(2) self.assertTrue(rk2.is_close(ops.Sgate())) rk3 = ops.Rk(3) self.assertTrue(rk3.is_close(ops.Tgate())) for idx in range(8): psi = state.zeros(2) psi = (ops.Rk(idx)**2 @ ops.Rk(-idx)**2)(psi) self.assertTrue(psi.is_close(state.zeros(2)))
def create_unitaries(base, limit): """Create all combinations of all base gates, up to length 'limit'.""" # Create bitstrings up to bitstring length limit-1: # 0, 1, 00, 01, 10, 11, 000, 001, 010, ... # # Multiply together the 2 base operators, according to their index. # Note: This can be optimized, by remembering the last 2^x results # and multiplying them with base gets 0, 1. # gate_list = [] for width in range(limit): for bits in helper.bitprod(width): U = ops.Identity() for bit in bits: U = U @ base[bit] gate_list.append(U) return gate_list
def test_v_vdag_v(self): """Figure 4.8 Nielson, Chuang.""" # Make Toffoli out of V = sqrt(X). # v = ops.Vgate() # Could be any unitary, in principle! ident = ops.Identity() cnot = ops.Cnot(0, 1) o0 = ident * ops.ControlledU(1, 2, v) c2 = cnot * ident o2 = (ident * ops.ControlledU(1, 2, v.adjoint())) o4 = ops.ControlledU(0, 2, v) final = o4 @ c2 @ o2 @ c2 @ o0 v2 = v @ v cv1 = ops.ControlledU(1, 2, v2) cv0 = ops.ControlledU(0, 1, cv1) self.assertTrue(final.is_close(cv0))
def test_bell(self): """Check successful entanglement by computing the schmidt_number.""" b00 = bell.bell_state(0, 0) self.assertGreater(b00.schmidt_number([1]), 1.0) self.assertTrue( b00.is_close((state.zeros(2) + state.ones(2)) / math.sqrt(2))) # Note the order is reversed from pictorials. op_exp = (ops.Cnot(0, 1) @ (ops.Hadamard() * ops.Identity())) b00_exp = op_exp(state.zeros(2)) self.assertTrue(b00.is_close(b00_exp)) b01 = bell.bell_state(0, 1) self.assertGreater(b01.schmidt_number([1]), 1.0) b10 = bell.bell_state(1, 0) self.assertGreater(b10.schmidt_number([1]), 1.0) b11 = bell.bell_state(1, 1) self.assertGreater(b11.schmidt_number([1]), 1.0)
def run_experiment(nbits, solutions) -> None: """Run full experiment for a given flavor of f().""" # Note that op_zero multiplies the diagonal elements of the operator by -1, # except for element [0][0]. This can be interpreted as "rotating around # the |00..)>" state. More pragmatically, multiplying this op_zero with # a Hadamard from the left and right gives a matrix of this form: # # 2/N-1 2/N 2/N ... 2/N # 2/N 2N-1 2/N ... 2/N # 2/N 2/N 2/N-1 ... 2/N # 2/N 2/N 2/N ... 2/N-1 # # Multiplying this matrix with a state vector computes exactly: # 2u - c_x # for every vector element c_x, with u being the mean over the # state vector. This is the defintion of inversion about the mean. # zero_projector = np.zeros((2**nbits, 2**nbits)) zero_projector[0, 0] = 1 op_zero = ops.Operator(zero_projector) # Make f and Uf. Note: # We reserve space for an ancilla 'y', which is unused in # Grover's algorithm. This allows reuse of the Deutsch Uf builder. # # We use the Oracle construction for convenience. It is rather # slow (full matrix) for larger qubit counts. Once can construct # a 'regular' function for the grover search algorithms, but this # function is different for each bitstring and that quickly gets # confusing. # f = make_f(nbits, solutions) uf = ops.OracleUf(nbits+1, f) # Build state with 1 ancilla of |1>. # psi = state.zeros(nbits) * state.ones(1) for i in range(nbits + 1): psi.apply1(ops.Hadamard(), i) # Build Grover operator, note Id() for the ancilla. # The Grover operator is the combination of: # - phase inversion via the u unitary # - inversion about the mean (see matrix above) # hn = ops.Hadamard(nbits) reflection = op_zero * 2.0 - ops.Identity(nbits) inversion = hn(reflection(hn)) * ops.Identity() grover = inversion(uf) # Number of Grover iterations # # There are at least 2 ways to compute the required number of iterations. # # 1) Most text books, eg., page 157 of Kaye, Laflamme and Mosca, find # that the highest probability of finding the right results occurs # after pi/4 sqrt(n) rotations. # # 2) For grover specifically, with 1 solution the iteration count is: # int(math.pi / 4 * math.sqrt(n)) # # 3) For grover with multiple solutions: # int(math.pi / 4 * math.sqrt(n / solutions)) # # 4) For amplitude amplification, it's the probability of good # solutions, which is trivial with the Grover equal # superposition here: # int(math.sqrt(n / solutions)) # iterations = int(math.pi / 4 * math.sqrt(2**nbits / solutions)) for _ in range(iterations): psi = grover(psi) # Measurement - pick element with higher probability. # # Note: We constructed the Oracle with n+1 qubits, to allow # for the 'xor-ancillary'. To check the result, we need to # ignore this ancilla. # maxbits, maxprob = psi.maxprob() result = f(maxbits[:-1]) print('Got f({}) = {}, want: 1, #: {:2d}, p: {:6.4f}' .format(maxbits[:-1], result, solutions, maxprob)) if result != 1: raise AssertionError('something went wrong, measured invalid state')
def run_experiment(nbits_phase, nbits_grover, solutions) -> None: """Run full experiment for a given number of solutions.""" # Building the Grover Operator, see grover.py n = 2**nbits_grover zero_projector = np.zeros((n, n)) zero_projector[0, 0] = 1 op_zero = ops.Operator(zero_projector) f = make_f(nbits_grover, solutions) u = ops.OracleUf(nbits_grover + 1, f) # The state for the counting algorithm. # We reserve nbits for the phase estimation. # We also reserve nbits for the Oracle. # These numbers could be adjusted to achieve better # accuracy. Yet, this keeps the code a little bit simpler, # while trading off a few off-by-1 estimation errors. # # We also add the |1> for the Oracle. # psi = (state.zeros(nbits_phase) * state.zeros(nbits_grover) * state.ones(1)) # Apply Hadamard to all the qubits. for i in range(nbits_phase + nbits_grover + 1): psi.apply(ops.Hadamard(), i) # Construct the Grover Operator. reflection = op_zero * 2.0 - ops.Identity(nbits_grover) hn = ops.Hadamard(nbits_grover) inversion = hn(reflection(hn)) * ops.Identity() grover = inversion(u) # Now that we have the Grover Operator, we have to perform # phase estimation. This loop is a copy from phase_estimation.py # with more comments there. # for idx, inv in enumerate(range(nbits_phase - 1, -1, -1)): u2 = grover for _ in range(idx): u2 = u2(u2) psi = ops.ControlledU(inv, nbits_phase, u2)(psi, inv) # Reverse QFT gives us the phase as a fraction of 2*Pi psi = ops.Qft(nbits_phase).adjoint()(psi) # Get the state with highest probability and compute the phase # as a binary fraction. Note that the probability increases # as M, the number of solutions, gets closer and closer to N, # the total mnumber of states. maxbits, maxprob = psi.maxprob() phi_estimate = sum(maxbits[i] * 2**(-i - 1) for i in range(nbits_phase)) # We know that after phase estimation, this holds: # # sin(phi/2) = sqrt(M/N) # M = N * sin(phi/2)^2 # # Hence we can compute M. We keep the result to 2 digit to visualize # the errors. Note that the phi_estimate is a fraction of 2*PI, hence # the 1/2 in above formula cancels out against the 2 and we compute: M = round(n * math.sin(phi_estimate * math.pi)**2, 2) print('Estimate: {:.4f} prob: {:5.2f}% --> M: {:5.2f}, want: {:2d}'.format( phi_estimate, maxprob * 100.0, M, solutions))
def run_experiment(nbits) -> None: """Run full experiment for a given flavor of f().""" hn = ops.Hadamard(nbits) h = ops.Hadamard() n = 2**nbits # Note that op_zero multiplies the diagonal elements of the operator by -1, # except for element [0][0]. This can be interpreted as "rotating around # the |00..)>" state. More pragmatically, multiplying this op_zero with # a Hadamard from the left and right gives a matrix of this form: # # 2/N-1 2/N 2/N ... 2/N # 2/N 2N-1 2/N ... 2/N # 2/N 2/N 2/N-1 ... 2/N # 2/N 2/N 2/N ... 2/N-1 # # Multiplying this matrix with a state vector computes exactly: # 2u - c_x # for every vector element c_x, with u being the mean over the # state vector. This is the defintion of inversion about the mean. # zero_projector = np.zeros((n, n)) zero_projector[0, 0] = 1 op_zero = ops.Operator(zero_projector) # Make f and Uf. Note: We reserve space for an ancilla 'y', which is unused # in Grover's algorithm. This allows reuse of the Deutsch Uf builder. # # We use the Oracle construction for convenience. It is rather slow (full # matrix) for larger qubit counts. Once can construct a 'regular' function # for the grover search algorithms, but this function is different for # each bitstring and that quickly gets quite confusing. # # Good examples for 2-qubit and 3-qubit functions can be found here: # https://qiskit.org/textbook/ch-algorithms/grover.html#3qubits # f = make_f(nbits) u = ops.OracleUf(nbits + 1, f) # Build state with 1 ancilla of |1>. # psi = state.zeros(nbits) * state.ones(1) for i in range(nbits + 1): psi.apply(h, i) # Build Grover operator, note Id() for the ancilla. # The Grover operator is the combination of: # - phase inversion via the u unitary # - inversion about the mean (see matrix above) # reflection = op_zero * 2.0 - ops.Identity(nbits) inversion = hn(reflection(hn)) * ops.Identity() grover = inversion(u) # Number of Grover iterations # # There are at least 2 ways to compute the required number of iterations. # # 1) Most text books, eg., page 157 of Kaye, Laflamme and Mosca, find # that the highest probability of finding the right results occurs # after pi/4 sqrt(n) rotations. # # 2) I found this computation in qcircuits: # 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)) # # Both produce identical results. # iterations = int(math.pi / 4 * math.sqrt(n)) for _ in range(iterations): psi = grover(psi) # Measurement - pick element with higher probability. # # Note: We constructed the Oracle with n+1 qubits, to allow # for the 'xor-ancillary'. To check the result, we need to # ignore this ancilla. # maxbits, maxprobs = psi.maxprob() result = f(maxbits[:-1]) print(f'Got f({maxbits[:-1]}) = {result}, want: 1') if result != 1: raise AssertionError('something went wrong, measured invalid state')
def test_reversible_hadamard(self): """H*H = I.""" h2 = ops.Hadamard(2) i2 = ops.Identity(2) self.assertTrue((h2 @ h2).is_close(i2))
def with_matmul(): psi = state.zeros(nbits) op = ops.Identity(qubit) * gate * ops.Identity(nbits - qubit - 1) psi = op(psi)
def test_swap(self): """Swap(Swap) == I.""" swap = ops.Swap(0, 3) self.assertTrue((swap @ swap).is_close(ops.Identity(4)))