Skip to content

What Happens When the Wrong Operations are Performed?

In our tests we (generally) assume that one side of the network can properly perform the desired operations. However, we do not know if the other computer has the desired functionality. If it does, then the tests will pass as intended. But what occurs if the computer does not have the required capabilities? This notebook shows, in code, several examples of the test results when the wrong operations are performed.

Run the cell below to import all the required modules/libraries.

from qiskit import QuantumCircuit, IBMQ, execute
from qiskit.providers.ibmq.managed import IBMQJobManager
from qiskit.quantum_info import random_unitary
from random import random
import numpy as np
import context
from device_independent_test import incompatible_measurement
from device_independent_test import quantum_communicator
from device_independent_test import entanglement
from device_independent_test import dimension

provider = IBMQ.load_account()

Dimensionality Testing

The dimensionality test determines if a computer can properly prepare a system of at least a certain dimension. In the code below, Alice wants to encode {0,1,2,3} into a two-qubit system and transmit this to Bob. What if Alice is unable to generate a two qubit system? Below Alice is unable to prepare a second qubit and can only encode one classical bit. Because of this, the probability that Bob can recover the classical information drops from 1 (disregarding noise) to 0.5.

# alice prepares the complete set of orthogonal states
qc_00 = dimension.prepare_bit_circuit([0,0])
qc_01 = dimension.prepare_bit_circuit([0,1])
qc_10 = dimension.prepare_bit_circuit([1,0])
qc_11 = dimension.prepare_bit_circuit([1,1])

circuits = [qc_00, qc_01, qc_10, qc_11]

# Bob measures in the computational basis.
for qc in circuits:
    qc.measure_all()

shots = 1000
d2_job_00 = execute(qc, backend=provider.get_backend('ibmq_qasm_simulator'), shots=shots)
d2_job_01 = execute(qc, backend=provider.get_backend('ibmq_qasm_simulator'), shots=shots)
d2_job_10 = execute(qc, backend=provider.get_backend('ibmq_qasm_simulator'), shots=shots)
d2_job_11 = execute(qc, backend=provider.get_backend('ibmq_qasm_simulator'), shots=shots)

counts_00 = d2_job_00.result().get_counts()
counts_01 = d2_job_01.result().get_counts()
counts_10 = d2_job_10.result().get_counts()
counts_11 = d2_job_11.result().get_counts()

# note that the outputs from the job are in the reverse order of the keys we're expecting
d2_p_succ_00 = counts_00["00"]/shots if ("00" in counts_00) else 0.0
d2_p_succ_01 = counts_01["10"]/shots if ("10" in counts_01) else 0.0
d2_p_succ_10 = counts_10["01"]/shots if "01" in counts_10 else 0.0 
d2_p_succ_11 = counts_11["11"]/shots if "11" in counts_11 else 0.0

d2_success_probability = (d2_p_succ_00 + d2_p_succ_01 + d2_p_succ_10 + d2_p_succ_11)/2

print("Success probability: ", d2_success_probability) # the classical bound is 0.5 
print("With 2 qubits, this test can succeed with probability 1.")
Success probability:  0.5
With 2 qubits, this test can succeed with probability 1.

Incompatible Measurement Testing

This module determines if a quantum device can perform incompatible measurements. Recall that incompatible measurments are those which do not commute, measuring in the Z and X bases, for example. Below the compatible measurement from the theory notebook is performed. Bob's first (y=0) projective measurement performs the following rotation (the global phase on the second basis state has been changed):

\begin{pmatrix} cos(\theta) & sin(\theta)\\ -sin(\theta) & cos(\theta)\\ \end{pmatrix}

Thus we can perform Bob's measurement via a Y rotation by - 2\theta then a measurment in the computational basis. Now let's construct the matrix for the compatible measurement. This is given by:

\begin{pmatrix} sin(\theta + \pi /4) & -cos(\theta + \pi /4)\\ cos(\theta + \pi /4) & sin(\theta + \pi /4)\\ \end{pmatrix}

Using a couple trig properties, this becomes

\begin{pmatrix} cos(\pi /4 -\theta) & -sin(\pi /4 -\theta)\\ sin(\pi / 4 -\theta) & cos(\pi /4 -\theta)\\ \end{pmatrix}

The above matrix is a rotation of 2(\pi /4 -\theta) around the Y axis. In the incompatible measurement test, we use \theta = \pi / 8. Thus, this measurement is achieved via a rotation of \pi / 4.

The code snippet below implements this compatible measurement. This set of projective measurements should not violate the classical bounds.

dispatch = quantum_communicator.LocalDispatcher([provider.get_backend('ibmq_qasm_simulator')])

# create bb84 states
pre_ops = [incompatible_measurement.bb84_states()]

# measure in the wrong basis
qc0 = QuantumCircuit(4)
qc0.u3(-np.pi/4,0,0,range(0,4))
qc0.measure_all()

qc1 = QuantumCircuit(4)
qc1.u3(np.pi/4,0,0,range(0,4)) # compatible basis
qc1.measure_all()

post_ops = [[QuantumCircuit(4)],[qc0,qc1]]
counts = dispatch.batch_run_and_transmit(pre_ops,post_ops,1000)
violation = incompatible_measurement.bell_violation(counts[0],counts[1],1000,1000)

print("Incompatible measurement test score: ", violation)
print("Classical bound is 6. Quantum systems score up to about 6.8.")
Incompatible measurement test score:  4.002
Classical bound is 6. Quantum systems score up to about 6.8.

Entanglement Testing

The CHSH test in this library truly relies both upon entanglement and incompatible measurments. For the purposes of this document, we will assume that the ability to perform incompatible measurments has already been verified. The code below demonstrates what happens if an entangled pair is not generated.

The first example generates random states and performs the CHSH measurments upon them (Alice projectively measuring in the Z and X bases, Bob in the W and V). A quantum system with entanglement should be able to achieve a test score of magnitude greater than 2 (the classical bound). As this test is generating random, non-entangled states (failing to perform even the desired one qubit gates), it fails to exceed the classical bounds.

dispatch = quantum_communicator.LocalDispatcher([provider.get_backend('ibmq_qasm_simulator')])
shots = 1000

# Generate two random states via random unitary operations
random_circ = random_unitary(2).tensor(random_unitary(2))
qc = QuantumCircuit(2)
qc.unitary(random_circ,[0,1],label='random unitaries')
qc.draw()

# Alice's measurements (Z or X basis)
alice_z = entanglement.alice_Z()
alice_z.measure(0,0)
alice_x = entanglement.alice_X()
alice_x.measure(0,0)

# Bob's measurements (W or V basis)
bob_w = entanglement.bob_W()
bob_w.measure(1,1)
bob_v = entanglement.bob_V()
bob_v.measure(1,1)

post_ops = [[alice_z,alice_x],[bob_w,bob_v]]

counts = dispatch.batch_run_and_transmit([qc],post_ops,shots) # run all combinations

expected_ZW = entanglement.compute_expectation_for_CHSH(counts[0], shots)
expected_ZV = entanglement.compute_expectation_for_CHSH(counts[1], shots)
expected_XW = entanglement.compute_expectation_for_CHSH(counts[2], shots)
expected_XV = entanglement.compute_expectation_for_CHSH(counts[3], shots)

test_val = (expected_ZW + expected_ZV + expected_XW - expected_XV)
print("CHSH test score: ", test_val)
print("Classical bound is of magnitude 2. Quantum systems can go up to about 2.828")
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-4-89c1530e5477> in <module>
     24 counts = dispatch.batch_run_and_transmit([qc],post_ops,shots) # run all combinations
     25 
---> 26 expected_ZW = entanglement.compute_expectation_for_CHSH(counts[0], shots)
     27 expected_ZV = entanglement.compute_expectation_for_CHSH(counts[1], shots)
     28 expected_XW = entanglement.compute_expectation_for_CHSH(counts[2], shots)


~/code/QiskitSummerJam2020/device_independent_test/entanglement.py in compute_expectation_for_CHSH(counts, shots)
     15 # @returns  float expectation value
     16 def compute_expectation_for_CHSH(counts, shots):
---> 17     return (counts["00"] + counts["11"] - counts["01"] - counts["10"])/shots
     18 
     19 # @brief    Runs a CHSH test (seperate circuits)


KeyError: '11'

As can be seen above, generating random states does not result in good test scores, dropping even below 1 frequently.

What happens, however, if the computer is able to perform the desired Bell state with a certain probability? The next failing case below can properly run single qubit gates and performs a hadamard on the first register. However, it then fails to generate entanglement through a CNOT with probability p \_ failure. This code creates runs versions of the CHSH test, each which may randomly fail to entangle the qubits. By increasing p \_ failure, the test score decreases.

dispatch = quantum_communicator.LocalDispatcher([provider.get_backend('ibmq_qasm_simulator')])
shots = 200 # shots per run
runs = 5 # number of possibly different circuits to run
p_failure = 0.2 # probability of failing to entangle

# Alice's measurements (Z or X basis)
alice_z = entanglement.alice_Z()
alice_z.measure(0,0)
alice_x = entanglement.alice_X()
alice_x.measure(0,0)

# Bob's measurements (W or V basis)
bob_w = entanglement.bob_W()
bob_w.measure(1,1)
bob_v = entanglement.bob_V()
bob_v.measure(1,1)

post_ops = [[alice_z,alice_x],[bob_w,bob_v]]

# each dictionary below corresponds to a different measurement: ZW,ZV,XW,XV
counts = [{"00":0,"11":0,"10":0,"01":0},{"00":0,"11":0,"10":0,"01":0},{"00":0,"11":0,"10":0,"01":0},{"00":0,"11":0,"10":0,"01":0}]

for i in range(0,runs): # run possibly different versions of the circuits
    qc = QuantumCircuit(2)
    qc.h(0)
    if random() > p_failure: # randomly fail to generate entanglement
        qc.cx(0,1)
    temp = dispatch.batch_run_and_transmit([qc],post_ops,shots) # run all combinations of operations
    for j in range (0,4):
        for key in counts[j]:
            counts[j][key] = counts[j][key]+temp[j].get(key,0.0) # add data to accumulating counts

expected_ZW = entanglement.compute_expectation_for_CHSH(counts[0], shots*runs)
expected_ZV = entanglement.compute_expectation_for_CHSH(counts[1], shots*runs)
expected_XW = entanglement.compute_expectation_for_CHSH(counts[2], shots*runs)
expected_XV = entanglement.compute_expectation_for_CHSH(counts[3], shots*runs)

test_val = (expected_ZW + expected_ZV + expected_XW - expected_XV)
print("CHSH test score: ", test_val)
print("Classical bound is of magnitude 2. Quantum systems can go up to about 2.828")

As this code demonstrates, when the computer often runs the correct circuit, it can still violate the classical bounds. This makes it more difficult to decide when the system fails a test. Our test modules expect tolerances for all tests. The tolerance of the result lets us decide how close to perfect functionality we require.