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 fulladder_matrix(psi: state.State): """Non-quantum-exploiting, classic full adder.""" psi = ops.Cnot(0, 3)(psi, 0) psi = ops.Cnot(1, 3)(psi, 1) psi = ops.ControlledU(0, 1, ops.Cnot(1, 4))(psi, 0) psi = ops.ControlledU(0, 2, ops.Cnot(2, 4))(psi, 0) psi = ops.ControlledU(1, 2, ops.Cnot(2, 4))(psi, 1) psi = ops.Cnot(2, 3)(psi, 2) return psi
def test_controlled_z(self): """Exercise 4.18 in Nielson, Chuang, CZ(0, 1) == CZ(1, 0).""" z0 = ops.ControlledU(0, 1, ops.PauliZ()) z1 = ops.ControlledU(1, 0, ops.PauliZ()) self.assertTrue(z0.is_close(z1)) z0 = ops.ControlledU(0, 7, ops.PauliZ()) z1 = ops.ControlledU(7, 0, ops.PauliZ()) self.assertTrue(z0.is_close(z1))
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(a1: np.complexfloating, a2: np.complexfloating, target: float) -> None: """Construct swap test circuit and measure.""" # The circuit is quite simple: # # |0> --- H --- o --- H --- Measure # | # a1 --------- x --------- # | # a2 ----------x --------- psi = state.bitstring(0) * state.qubit(a1) * state.qubit(a2) psi = ops.Hadamard()(psi, 0) psi = ops.ControlledU(0, 1, ops.Swap(1, 2))(psi) psi = ops.Hadamard()(psi, 0) # Measure once. p0, _ = ops.Measure(psi, 0) if abs(p0 - target) > 0.05: raise AssertionError( 'Probability {:.2f} off more than 5% from target {:.2f}'.format( p0, target)) print('Similarity of a1: {:.2f}, a2: {:.2f} ==> %: {:.2f}'.format( a1, a2, 100.0 * p0))
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_controlled_controlled(self): """Toffoli gate over 4 qubits to verify that controlling works.""" cnot = ops.Cnot(0, 3) toffoli = ops.ControlledU(0, 1, cnot) self.assertTrue(toffoli.is_close(ops.Toffoli(0, 1, 4))) psi = toffoli(state.bitstring(0, 1, 0, 0, 1)) self.assertTrue(psi.is_close(state.bitstring(0, 1, 0, 0, 1))) psi = toffoli(state.bitstring(1, 1, 0, 0, 1)) self.assertTrue(psi.is_close(state.bitstring(1, 1, 0, 0, 0))) psi = toffoli(state.bitstring(0, 0, 1, 1, 0, 0, 1), idx=2) self.assertTrue(psi.is_close(state.bitstring(0, 0, 1, 1, 0, 0, 0)))
def phase1(psi, u, t): """Unpack binary fraction.""" # Unpack the binary fractions of the phase into the first t qubits. # # t qubits # |0> - H -------------------- ... ----o -- # |0> - H -----------------o-- ... --- | -- # |0> - H ---------o-------|-- ... --- | -- # |0> - H -o-------|-------|-- ... --- | -- # | | | | # nbits qubits | | | # |u> --- U^1 --- U^2 --- U^4 ... --- U^s^(t-1) # psi = ops.Hadamard(t)(psi) for idx, inv in enumerate(range(t - 1, -1, -1)): u2 = u for _ in range(idx): u2 = u2(u2) psi = ops.ControlledU(inv, t, u2)(psi, inv) return psi
def expo_u(psi: state.State, u: ops.Operator, t: int) -> state.State: """Exponentiate U.""" # Unpack the binary fractions of the phase into the first t qubits. # # t qubits # |0> - H -------------------- ... ----o -- # |0> - H -----------------o-- ... --- | -- # |0> - H ---------o-------|-- ... --- | -- # |0> - H -o-------|-------|-- ... --- | -- # | | | | # nbits qubits | | | # |u> --- U^1 --- U^2 --- U^4 ... --- U^s^(t-1) # psi = ops.Hadamard(t)(psi) for idx, inv in enumerate(range(t - 1, -1, -1)): u2 = u for _ in range(idx): u2 = u2(u2) psi = ops.ControlledU(inv, t, u2)(psi, inv) return psi
def simple_kick(): psi = state.bitstring(0, 0, 1) psi = ops.Hadamard(2)(psi) psi = ops.ControlledU(0, 2, ops.Sgate())(psi) psi = ops.ControlledU(1, 2, ops.Tgate())(psi, 1) psi.dump()
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 test_controlled_rotations(self): psi = state.bitstring(1, 1, 1) psi02 = ops.ControlledU(0, 2, ops.Rk(1))(psi) psi20 = ops.ControlledU(2, 0, ops.Rk(1))(psi) self.assertTrue(psi02.is_close(psi20))