def gc_decomp(U): """Group commutator decomposition.""" def diagonalize(U): _, V = np.linalg.eig(U) return ops.Operator(V) # Get axis and theta for the operator. axis, theta = u_to_bloch(U) # The angle phi comes from eq 10 in 'The Solovay-Kitaev Algorithm' by # Dawson, Nielsen. It is fully derived in the book section on the # theorem and algorithm. phi = 2.0 * np.arcsin(np.sqrt( np.sqrt((0.5 - 0.5 * np.cos(theta / 2))))) V = ops.RotationX(phi) if axis[2] > 0: W = ops.RotationY(2 * np.pi - phi) else: W = ops.RotationY(phi) Ud = diagonalize(U) VWVdWdd = diagonalize(V @ W @ V.adjoint() @ W.adjoint()) S = Ud @ VWVdWdd.adjoint() V_hat = S @ V @ S.adjoint() W_hat = S @ W @ S.adjoint() return V_hat, W_hat
def gc_decomp(U): """Group Commutator Decomposition.""" def diagonalize(U): _, V = np.linalg.eig(U) return ops.Operator(V) # Because of moderate numerical instability, it can happen # that the trace is just a tad over 2.000000. If this happens, # we tolerate it and set the trace to exactly 2.000000. tr = np.trace(U) if tr > 2.0: tr = 2.0 # We know how to compute theta from u_to_bloch(). theta = 2.0 * np.arccos(np.real(tr / 2)) # The angle phi comes from eq 10 in 'The Solovay-Kitaev Algorithm' by # Dawson, Nielsen. phi = 2.0 * np.arcsin(np.sqrt(np.sqrt((0.5 - 0.5 * np.cos(theta / 2))))) axis, _ = u_to_bloch(U) V = ops.RotationX(phi) if axis[2] < 0: W = ops.RotationY(2 * np.pi - phi) else: W = ops.RotationY(phi) V1 = diagonalize(U) V2 = diagonalize(V @ W @ V.adjoint() @ W.adjoint()) S = V1 @ V2.adjoint() V_tilde = S @ V @ S.adjoint() W_tilde = S @ W @ S.adjoint() return V_tilde, W_tilde
def main(argv): if len(argv) > 1: raise app.UsageError('Too many command-line arguments.') num_experiments = 10 depth = 8 recursion = 4 print('SK algorithm - depth: {}, recursion: {}, experiments: {}'. format(depth, recursion, num_experiments)) base = [to_su2(ops.Hadamard()), to_su2(ops.Tgate())] gates = create_unitaries(base, depth) sum_dist = 0.0 for i in range(num_experiments): 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())) U_approx = sk_algo(U, gates, recursion) dist = trace_dist(U, U_approx) sum_dist += dist phi1 = U(state.zero) phi2 = U_approx(state.zero) print('[{:2d}]: Trace Dist: {:.4f} State: {:6.4f}%'. format(i, dist, 100.0 * (1.0 - np.real(np.dot(phi1, phi2.conj()))))) print('Gates: {}, Mean Trace Dist:: {:.4f}'. format(len(gates), sum_dist / num_experiments))
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 ry(self, idx: int, theta: float): self.apply1(ops.RotationY(theta), idx, 'ry', val=theta)
def make_u(alpha, beta, gamma, delta): """Construct unitary from the 4 parameters.""" return ( (ops.RotationZ(beta) @ ops.RotationY(gamma) @ ops.RotationZ(delta)) * cmath.exp(1.0j * alpha))
def ry(self, idx, theta): self.apply1(ops.RotationY(theta), idx, 'ry', val=theta)