def test_dft_adjoint(self): bits = [0, 1, 0, 1, 1, 0] psi = state.bitstring(*bits) psi = ops.Qft(6)(psi) psi = ops.Qft(6).adjoint()(psi) maxbits, _ = psi.maxprob() self.assertEqual(maxbits, tuple(bits))
def run_experiment(nbits, t=8): """Run single phase estimation experiment.""" # Make a unitary and find Eigen value/vector to estimate. # umat = scipy.stats.unitary_group.rvs(2**nbits) eigvals, eigvecs = np.linalg.eig(umat) u = ops.Operator(umat) # Pick Eigenvalue 'eigen_index' (any Eigenvalue / Eigenvector pair will work). eigen_index = 1 phi = np.real(np.log(eigvals[eigen_index]) / (2j * np.pi)) if phi < 0: phi += 1 # Make state + circuit to estimate phi. # Pick Eigenvector 'eigen_index' to math the Eigenvalue. psi = state.zeros(t) * state.State(eigvecs[:, eigen_index]) psi = phase1(psi, u, t) psi = ops.Qft(t).adjoint()(psi) # Find state with highest measurement probability and show results. # maxbits, maxprob = psi.maxprob() phi_estimate = sum(maxbits[i] * 2**(-i - 1) for i in range(t)) delta = abs(phi - phi_estimate) print('Phase : {:.4f}'.format(phi)) print('Estimate: {:.4f} delta: {:.4f} probability: {:5.2f}%'.format( phi_estimate, delta, maxprob * 100.0)) if delta > 0.02 and phi_estimate < 0.98: print('*** Warning: Delta is large')
def test_bloch_coords(self): psi = state.bitstring(1, 1) psi = ops.Qft(2)(psi) rho0 = ops.TraceOut(psi.density(), [1]) rho1 = ops.TraceOut(psi.density(), [0]) x0, _, _ = helper.density_to_cartesian(rho0) _, y1, _ = helper.density_to_cartesian(rho1) self.assertTrue(math.isclose(-1.0, x0, abs_tol=0.01)) self.assertTrue(math.isclose(-1.0, y1, abs_tol=0.01))
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_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))