Beispiel #1
0
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))
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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())))))
Beispiel #5
0
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.')
Beispiel #6
0
    def test_global_phase(self):
        """Exercise 4.14 in Nielson, Chuang, HTH == phase*rotX(pi/4)."""

        h = ops.Hadamard()
        op = h(ops.Tgate()(h))

        # If equal up to a global phase, all values should be equal.
        phase = op / ops.RotationX(math.pi / 4)
        self.assertTrue(
            math.isclose(phase[0, 0].real, phase[0, 1].real, abs_tol=1e-6))
        self.assertTrue(
            math.isclose(phase[0, 0].imag, phase[0, 1].imag, abs_tol=1e-6))
        self.assertTrue(
            math.isclose(phase[0, 0].real, phase[1, 0].real, abs_tol=1e-6))
        self.assertTrue(
            math.isclose(phase[0, 0].imag, phase[1, 0].imag, abs_tol=1e-6))
        self.assertTrue(
            math.isclose(phase[0, 0].real, phase[1, 1].real, abs_tol=1e-6))
        self.assertTrue(
            math.isclose(phase[0, 0].imag, phase[1, 1].imag, abs_tol=1e-6))
Beispiel #7
0
    def test_control_equalities(self):
        """Exercise 4.31 Nielson, Chung."""

        i, x, y, z = ops.Pauli()
        x1 = x * i
        x2 = i * x
        y1 = y * i
        y2 = i * y
        z1 = z * i
        z2 = i * z
        c = ops.Cnot(0, 1)
        theta = 25.0 * math.pi / 180.0
        rx2 = i * ops.RotationX(theta)
        rz1 = ops.RotationZ(theta) * i

        self.assertTrue(c(x1(c)).is_close(x1(x2)))
        self.assertTrue((c @ x1 @ c).is_close(x1 @ x2))
        self.assertTrue((c @ y1 @ c).is_close(y1 @ x2))
        self.assertTrue((c @ z1 @ c).is_close(z1))
        self.assertTrue((c @ x2 @ c).is_close(x2))
        self.assertTrue((c @ y2 @ c).is_close(z1 @ y2))
        self.assertTrue((c @ z2 @ c).is_close(z1 @ z2))
        self.assertTrue((rz1 @ c).is_close(c @ rz1))
        self.assertTrue((rx2 @ c).is_close(c @ rx2))
Beispiel #8
0
 def rx(self, idx: int, theta: float):
     self.apply1(ops.RotationX(theta), idx, 'rx', val=theta)
Beispiel #9
0
    def test_double_rot(self):
        """Make sure rotations add up."""

        rx = ops.RotationX(45.0 / 180.0 * math.pi)
        self.assertTrue(
            (rx @ rx).is_close(ops.RotationX(90.0 / 180.0 * math.pi)))
Beispiel #10
0
 def rx(self, idx, theta):
     self.apply1(ops.RotationX(theta), idx, 'rx', val=theta)