Examples

Producing Bell states

Example code producing each of the four entangled Bell states for a two-qubit system.

The circuit diagram is

_images/bell.png

where |x⟩ and |y⟩ are each one of the computational basis states, |0⟩ or |1⟩.

E.g., \(|\beta_{00}⟩ = \frac{1}{\sqrt{2}} (|00⟩ + |11⟩)\).

Code:

import qcircuits as qc
from itertools import product


# Creates each of the four Bell states


def bell_state(x, y):
    H = qc.Hadamard()
    CNOT = qc.CNOT()

    phi = qc.bitstring(x, y)
    phi = H(phi, qubit_indices=[0])

    return CNOT(phi)


if __name__ == '__main__':

    for x, y in product([0, 1], repeat=2):

        print('\nInput: {} {}'.format(x, y))
        print('Bell state:')
        print(bell_state(x, y))

Quantum teleportation

_images/teleport.png

Code:

import qcircuits as qc


# Quantum Teleportation: transmitting two classical bits to transport a qubit state
# Alice has a qubit in a given quantum state.
# Alice and Bob have previously prepared a Bell state, and have since
# physically separated the qubits.
# Alice manipulates her hidden qubit and her half of the Bell state, and then
# measures both qubits.
# She sends the result (two classical bits) to Bob, who is able to reconstruct
# Alice's state by applying operators based on the measurement outcomes.


def quantum_teleportation(alice_state):
    # Get operators we will need
    CNOT = qc.CNOT()
    H = qc.Hadamard()
    X = qc.PauliX()
    Z = qc.PauliZ()

    # The prepared, shared Bell state
    bell = qc.bell_state(0, 0)
    # The whole state vector
    phi = alice_state * bell

    # Apply CNOT and Hadamard gate
    phi = CNOT(phi, qubit_indices=[0, 1])
    phi = H(phi, qubit_indices=[0])

    # Measure the first two bits
    # The only uncollapsed part of the state vector is Bob's
    M1, M2 = phi.measure(qubit_indices=[0, 1], remove=True)

    # Apply X and/or Z gates to third qubit depending on measurements
    if M2:
        phi = X(phi)
    if M1:
        phi = Z(phi)

    return phi


if __name__ == '__main__':
    # Alice's original state to be teleported to Bob
    alice = qc.qubit(theta=1.5, phi=0.5, global_phase=0.2)

    # Bob's state after quantum teleportation
    bob = quantum_teleportation(alice)

    print('Original state:', alice)
    print('\nTeleported state:', bob)

Quantum parallelism

_images/parallel.png

Code:

import qcircuits as qc
import numpy as np


# Example of quantum parallelism


# Construct a Boolean function
def construct_problem():
    answers = np.random.randint(0, 2, size=2)

    def f(bit):
        return answers[bit]

    return f


def quantum_parallelism(f):
    U_f = qc.U_f(f, d=2)
    H = qc.Hadamard()

    phi = qc.zeros(2)
    phi = H(phi, qubit_indices=[0])
    phi = U_f(phi)


if __name__ == '__main__':
    f = construct_problem()

    quantum_parallelism(f)

Deutsch’s algorithm

_images/deutsch.png

Code:

import qcircuits as qc
import numpy as np


# Deutsch's Algorithhm:
# We use interference to determine if f(0) = f(1) using a single function evaluation.


# Construct a Boolean function that is constant or balanced
def construct_problem():
    answers = np.random.randint(0, 2, size=2)

    def f(bit):
        return answers[bit]

    return f


def deutsch_algorithm(f):
    U_f = qc.U_f(f, d=2)
    H = qc.Hadamard()

    phi = H(qc.zeros()) * H(qc.ones())
    phi = U_f(phi)
    phi = H(phi, qubit_indices=[0])

    measurement = phi.measure(qubit_indices=0)
    return measurement


if __name__ == '__main__':
    f = construct_problem()
    parity = f(0) == f(1)

    measurement = deutsch_algorithm(f)

    print('f(0): {}, f(1): {}'.format(f(0), f(1)))
    print('f(0) == f(1): {}'.format(parity))
    print('Measurement: {}'.format(measurement))

The Deutsch-Jorza algorithm

_images/deutsch_jorza.png

Code:

import qcircuits as qc
import numpy as np
import random


# Deutsch-Jorza Algorithhm:
# We are presented with a Boolean function that is either constant or
# balanced (i.e., 0 for half of inputs, 1 for the other half).
# We make use of interference to determine whether the function is constant
# or balanced in a single function evaluation.


# Construct a Boolean function that is constant or balanced
def construct_problem(d=1, problem_type='constant'):
    num_inputs = 2**d
    answers = np.zeros(num_inputs, dtype=np.int32)

    if problem_type == 'constant':
        answers[:] = int(np.random.random() < 0.5)
    else: # function is balanced
        indices = np.random.choice(num_inputs, size=num_inputs//2, replace=False)
        answers[indices] = 1

    def f(*bits):
        index = sum(v * 2**i for i, v in enumerate(bits))

        return answers[index]

    return f


def deutsch_jorza_algorithm(d, f):
    # The operators we will need
    U_f = qc.U_f(f, d=d+1)
    H_d = qc.Hadamard(d)
    H = qc.Hadamard()

    state = qc.zeros(d) * qc.ones(1)
    state = (H_d * H)(state)
    state = U_f(state)
    state = H_d(state, qubit_indices=range(d))

    measurements = state.measure(qubit_indices=range(d))
    return measurements


if __name__ == '__main__':
    d = 10
    problem_type = random.choice(['constant', 'balanced'])

    f = construct_problem(d, problem_type)
    measurements = deutsch_jorza_algorithm(d, f)

    print('Problem type: {}'.format(problem_type))
    print('Measurement: {}'.format(measurements))
    print('Observed all zeros: {}'.format(not any(measurements)))

Superdense coding

_images/superdense.png

Code:

import qcircuits as qc
import numpy as np


# Superdense Coding: transmitting a qubit to transport two classical bits
# Alice and Bob have previously prepared a Bell state, and have since
# physically separated the qubits.
# Alice has two classical bits she wants to transmit to Bob.
# She manipulates her half of the Bell state depending on the values of those bits,
# then transmits her qubit to Bob, who then measures the system.


def superdense_coding(bit_1, bit_2):
    # Get operators we will need
    CNOT = qc.CNOT()
    H = qc.Hadamard()
    X = qc.PauliX()
    Z = qc.PauliZ()

    # The prepared, shared Bell state
    # Initially, half is in Alice's possession, and half in Bob's
    phi = qc.bell_state(0, 0)

    # Alice manipulates her qubit
    if bit_2:
        phi = X(phi, qubit_indices=[0])
    if bit_1:
        phi = Z(phi, qubit_indices=[0])

    # Bob decodes the two bits
    phi = CNOT(phi)
    phi = H(phi, qubit_indices=[0])
    measurements = phi.measure()
    return measurements


if __name__ == '__main__':
    # Alice's classical bits she wants to transmit
    bit_1, bit_2 = np.random.randint(0, 2, size=2)
    
    # Bob's measurements
    measurements = superdense_coding(bit_1, bit_2)
    
    print("Alice's initial bits:\t{}, {}".format(bit_1, bit_2))
    print("Bob's measurements:\t{}, {}".format(measurements[0], measurements[1]))