Ejemplo n.º 1
0
    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))
Ejemplo n.º 2
0
def run_single_qubit_mult():
    """Run experiments with single qubits."""

    # Construct Hamiltonian.
    hamil = (random.random() * ops.PauliX() + random.random() * ops.PauliY() +
             random.random() * ops.PauliZ())
    # Compute known minimum eigenvalue.
    eigvals = np.linalg.eigvalsh(hamil)

    # Brute force over the Bloch sphere.
    min_val = 1000.0
    for i in range(0, 180, 10):
        for j in range(0, 180, 10):
            theta = np.pi * i / 180.0
            phi = np.pi * j / 180.0

            # Build the ansatz with two rotation gates.
            ansatz = single_qubit_ansatz(theta, phi)

            # Compute <psi ! H ! psi>. Find smallest one, which will be
            # the best approximation to the minimum eigenvalue from above.
            # In this version, we just multiply out the result.
            psi = np.dot(ansatz.psi.adjoint(), hamil(ansatz.psi))
            if psi < min_val:
                min_val = psi

    # Result from brute force approach:
    print('Minimum: {:.4f}, Estimated: {:.4f}, Delta: {:.4f}'.format(
        eigvals[0], np.real(min_val), np.real(min_val - eigvals[0])))
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    def test_bell_and_pauli(self):
        b00 = bell.bell_state(0, 0)

        bell_xz = ops.PauliX()(b00)
        bell_xz = ops.PauliZ()(bell_xz)

        bell_iy = (1j * ops.PauliY())(b00)

        self.assertTrue(np.allclose(bell_xz, bell_iy))
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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')
Ejemplo n.º 8
0
def alice_measures(alice: state.State, expect0: np.complexfloating,
                   expect1: np.complexfloating, qubit0: np.complexfloating,
                   qubit1: np.complexfloating):
    """Force measurement and get teleported qubit."""

    # Alices measure her state and get a collapsed |qubit0 qubit1>.
    # She let's Bob know which one of the 4 combinations she obtained.

    # We force measurement here, collapsing to a state with the
    # first two qubits collapsed. Bob's qubit is still unmeasured.
    _, alice0 = ops.Measure(alice, 0, tostate=qubit0)
    _, alice1 = ops.Measure(alice0, 1, tostate=qubit1)

    # Depending on what was measured and communicated, Bob has to
    # one of these things to his qubit2:
    if qubit0 == 0 and qubit1 == 0:
        pass
    if qubit0 == 0 and qubit1 == 1:
        alice1 = ops.PauliX()(alice1, idx=2)
    if qubit0 == 1 and qubit1 == 0:
        alice1 = ops.PauliZ()(alice1, idx=2)
    if qubit0 == 1 and qubit1 == 1:
        alice1 = ops.PauliX()(ops.PauliZ()(alice1, idx=2), idx=2)

    # Now Bob measures his qubit (2) (without collapse, so we can
    # 'measure' it twice. This is not necessary, but good to double check
    # the maths).
    p0, _ = ops.Measure(alice1, 2, tostate=0, collapse=False)
    p1, _ = ops.Measure(alice1, 2, tostate=1, collapse=False)

    # Alice should now have 'teleported' the qubit in state 'x'.
    # We sqrt() the probability, we want to show (original) amplitudes.
    bob_a = math.sqrt(p0.real)
    bob_b = math.sqrt(p1.real)
    print('Teleported (|{:d}{:d}>)   a={:.2f}, b={:.2f}'.format(
        qubit0, qubit1, bob_a, bob_b))

    if (not math.isclose(expect0, bob_a, abs_tol=1e-6)
            or not math.isclose(expect1, bob_b, abs_tol=1e-6)):
        raise AssertionError('Invalid result.')
Ejemplo n.º 9
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.')
Ejemplo n.º 10
0
    def test_equalities(self):
        """Exercise 4.13 in Nielson, Chuang."""

        _, x, y, z = ops.Pauli()
        h = ops.Hadamard()

        op = h(x(h))
        self.assertTrue(op.is_close(ops.PauliZ()))

        op = h(y(h))
        self.assertTrue(op.is_close(-1.0 * ops.PauliY()))

        op = h(z(h))
        self.assertTrue(op.is_close(ops.PauliX()))

        op = x(z)
        self.assertTrue(op.is_close(1.0j * ops.PauliY()))
Ejemplo n.º 11
0
  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)))
Ejemplo n.º 12
0
    def test_acceleration(self):
        psi = state.bitstring(1, 0, 1, 0)
        qc = circuit.qc()
        qc.bitstring(1, 0, 1, 0)

        for i in range(4):
            qc.x(i)
            psi.apply(ops.PauliX(), i)
            qc.y(i)
            psi.apply(ops.PauliY(), i)
            qc.z(i)
            psi.apply(ops.PauliZ(), i)
            qc.h(i)
            psi.apply(ops.Hadamard(), i)
            if i:
                qc.cu1(0, i, 1.1)
                psi.apply_controlled(ops.U1(1.1), 0, i)

        if not psi.is_close(qc.psi):
            raise AssertionError('Numerical Problems')

        psi = state.bitstring(1, 0, 1, 0, 1)
        qc = circuit.qc()
        qc.bitstring(1, 0, 1, 0, 1)

        for n in range(5):
            qc.h(n)
            psi.apply(ops.Hadamard(), n)
            for i in range(0, 5):
                qc.cu1(n - (i + 1), n, math.pi / float(2**(i + 1)))
                psi.apply_controlled(ops.U1(math.pi / float(2**(i + 1))),
                                     n - (i + 1), n)
            for i in range(0, 5):
                qc.cu1(n - (i + 1), n, -math.pi / float(2**(i + 1)))
                psi.apply_controlled(ops.U1(-math.pi / float(2**(i + 1))),
                                     n - (i + 1), n)
            qc.h(n)
            psi.apply(ops.Hadamard(), n)

        if not psi.is_close(qc.psi):
            raise AssertionError('Numerical Problems')
Ejemplo n.º 13
0
def run_single_qubit_measure():
    """Run measurement experiments with single qubits."""

    # Construct Hamiltonian.
    a = random.random()
    b = random.random()
    c = random.random()
    hamil = (a * ops.PauliX() + b * ops.PauliY() + c * ops.PauliZ())

    # Compute known minimum eigenvalue.
    eigvals = np.linalg.eigvalsh(hamil)

    min_val = 1000.0
    for i in range(0, 360, 5):
        for j in range(0, 180, 5):

            theta = np.pi * i / 360.0
            phi = np.pi * j / 180.0

            # X Basis
            qc = single_qubit_ansatz(theta, phi)
            qc.h(0)
            val_a = a * qc.pauli_expectation(0)

            # Y Basis
            qc = single_qubit_ansatz(theta, phi)
            qc.sdag(0)
            qc.h(0)
            val_b = b * qc.pauli_expectation(0)

            # Z Basis
            qc = single_qubit_ansatz(theta, phi)
            val_c = c * qc.pauli_expectation(0)

            expectation = val_a + val_b + val_c
            if expectation < min_val:
                min_val = expectation

    print('Minimum eigenvalue: {:.3f}, Delta: {:.3f}'.format(
        eigvals[0], min_val - eigvals[0]))
Ejemplo n.º 14
0
    def test_s_gate(self):
        """S^2 == Z."""

        x = ops.Sgate() @ ops.Sgate()
        self.assertTrue(x.is_close(ops.PauliZ()))
Ejemplo n.º 15
0
 def cz(self, idx0, idx1):
     self.apply_controlled(ops.PauliZ(), idx0, idx1, 'cz')
Ejemplo n.º 16
0
 def z(self, idx: int):
     self.apply1(ops.PauliZ(), idx, 'z')
Ejemplo n.º 17
0
 def cz(self, idx0: int, idx1: int):
     self.applyc(ops.PauliZ(), idx0, idx1, 'cz')
Ejemplo n.º 18
0
def two_qubit():
    """Compute Pauli representation for two-qubit system."""

    for iteration in range(10):

        # First we construct a circuit with two, very random qubits.
        #
        qc = circuit.qc('random qubit')
        qc.qubit(random.random())
        qc.qubit(random.random())

        # Potentially entangle them.
        qc.h(0)
        qc.cx(0, 1)

        # Additionally rotate around randomly.
        for i in range(2):
            qc.rx(i, math.pi * random.random())
            qc.ry(i, math.pi * random.random())
            qc.rz(i, math.pi * random.random())

        # Compute density matrix.
        rho = qc.psi.density()

        # Every rho can be put in the 2-qubit Pauli representation,
        # which is this Sum over i, j from 0 to 3 inclusive, representing
        # the four Pauli matrices (including the Identity):
        #
        #                 3
        #    rho = 1/4 * Sum(c_ij * Pauli_i kron Pauli_j)
        #                i,j=0
        #
        # To compute the various factors c_ij, we multiply the Pauli
        # tensor products with the density matrix and take the trace. This
        # trace is the computed factor:
        #
        paulis = [ops.Identity(), ops.PauliX(), ops.PauliY(), ops.PauliZ()]
        c = np.zeros((4, 4), dtype=np.complex64)
        for i in range(4):
            for j in range(4):
                tprod = paulis[i] * paulis[j]
                c[i][j] = np.trace(rho @ tprod)

        # To test whether the two qubits are entangled, the diagonal factors
        # (without c[0][0]) are added up. If the sum is < 1.0, the qubit
        # states are still seperable.
        #
        diag = np.abs(c[1][1]) + np.abs(c[2][2]) + np.abs(c[3][3])
        print(f'{iteration}: diag: {diag:5.2f} ', end='')
        if diag > 1.0:
            print('--> Entangled')
        else:
            print('Seperable')

        # Let's verify the result and construct a density matrix
        # from the Pauli matrices using the computed factors:
        #
        new_rho = np.zeros((4, 4), dtype=np.complex64)
        for i in range(4):
            for j in range(4):
                tprod = paulis[i] * paulis[j]
                new_rho = new_rho + c[i][j] * tprod

        if not np.allclose(rho, new_rho / 4, atol=1e-5):
            raise AssertionError('Invalid Pauli Representation')
Ejemplo n.º 19
0
 def test_unitary(self):
   self.assertTrue(ops.PauliX().is_unitary())
   self.assertTrue(ops.PauliY().is_unitary())
   self.assertTrue(ops.PauliZ().is_unitary())
   self.assertTrue(ops.Identity().is_unitary())