# Unit testing for quantum programs?

Does unit testing on real quantum computers make sense as any observation in the quantum system can cause the system to loose its superposition state?

• I don't know about unit tests - the probabilistic nature of quantum computers makes it hard to create deterministic tests. but you could verify if the circuit is correct in the mathematical sense. Dec 24 '19 at 8:25

From what I know, no testing framework exists yet for quantum computing. I searched approximately one year ago for one and did not find anything.

But this does not mean you can't test!

Below are the methods I ended up using when I saw that no specialised testing framework for quantum computing was out there in the wild.

# Unitary simulation

This method can only be used for small circuits. It uses $$2^{2n + 8 + 1}$$ octets of memory, $$n$$ being the number of qubits used by the circuit. With 8Go of RAM, you can use this method up to $$\approx 11$$ qubits. A computer with 32Go of RAM can go up to $$\approx 12$$ qubits.

## 1. Circuits given by unitary matrices (no ancilla)

Imagine that some quantum algorithm requires me to implement the operation $$e^{-iZ\otimes F t}$$ for $$Z$$ Pauli $$\sigma_z$$ gate and $$F$$ a known Hermitian matrix.

I implemented this gate with a quantum circuit $$C$$ that does not need any ancilla qubit and I now want to check that my implementation implements correctly (maybe up to a given error) $$e^{-iZ\otimes Ft}$$. As the quantum circuit $$C$$ is assumed to act on a small number of qubits (remember, circuits are assumed small in this section), I can use a little linear algebra to compute the unitary matrix that $$C$$ implements.

Hopefully, some frameworks already implement the linear algebra part. For example qiskit has a unitary simulator.

The final test code would look like this in pseudo-code (as it is not specialised to one testing framework nor to one quantum computing framework):

import numpy

qubit_number = 5
absolute_error_tol = 1e-3
relative_error_tol = 1e-3

qcirc = get_quantum_circuit(qubit_number)
implemented_unitary = unitary_simulator(qcirc).get_unitary()
expected_unitary = numpy.zeros((2**qubit_number, 2**qubit_number),
dtype=numpy.complex)

assert numpy.allclose(
implemented_unitary,
expected_unitary,
atol=absolute_error_tol,
rtol=relative_error_tol
)


## 2. Circuit given by unitary matrices (with ancillas)

To test quantum circuits that require ancilla qubits, you should "separate" the ancillas from the meaningful qubits.

Here I assume that all the ancillas are pushed to the end, i.e. the last qubits are all ancillas. For example: $$\vert x \rangle \vert v(x) \rangle \vert a \rangle$$. If it is not the case, you will need to change the indexing of the example program at the end of this section according to your quantum register layout.

Now that there are ancillas we have something else to check: are all the ancillas returned to $$\vert 0 \rangle$$ at the end of my quantum circuit? In other words, the action of my quantum circuit on the ancillas must be the identity matrix.

The pseudo-code for this kind of quantum circuits:

import numpy

qubit_number = 5
ancillas_number = 3
absolute_error_tol = 1e-3
relative_error_tol = 1e-3

qcirc = get_quantum_circuit(qubit_number, ancillas_number)
implemented_unitary = unitary_simulator(qcirc).get_unitary()
expected_unitary = numpy.zeros((2**qubit_number, 2**qubit_number),
dtype=numpy.complex)

# The implemented_unitary is a matrix of size
# (2**(qubit_number + ancillas_number), 2**(qubit_number + ancillas_number))
# whereas the expected_unitary is of size
# (2**(qubit_number), 2**(qubit_number)). In order to compare the
# 2 matrices, we should "upgrade" the expected_unitary with identity matrices.
expected_unitary = numpy.kron(expected_unitary, numpy.identity(2**ancillas_number))

assert numpy.allclose(
implemented_unitary,
expected_unitary,
atol=absolute_error_tol,
rtol=relative_error_tol
)


# Statevector simulation

For simulable circuits. It uses $$2^{n + 4 + 1}$$ octets of memory, $$n$$ being the number of qubits used by the circuit. With 8Go of RAM you can use this method up to $$\approx 27$$ qubits. A computer with 32Go of RAM can go up to $$\approx 29$$ qubis. Qiskit's online simulator ibmq_qasm_simulator can go up to $$32$$ qubits.

## 1. Circuit given by unitary matrix that use a lot of ancillas

The unitary matrix simulation method is usefull, but cannot be used for medium-sized and large quantum circuits because of its large memory footprint.

When the tested quantum circuit has a lot of "garbage" qubits (i.e. ancillas), a little trick can allow us to test it, even if the quantum circuit needs $$n + a > 20$$ qubits (where $$a$$ is the number of ancilla qubits and $$n$$ is the number of "interesting" qubits, i.e. the total number of qubits minus the number of ancillas).

The trick is the following: as ancilla qubits should be given in the $$\vert 0 \rangle$$ state and returned to the same state, the input space of your quantum circuit is reduced from $$2^{n+a}$$ (the number of input states possible when counting the ancilla qubits) to $$2^{n}$$ (the number of input states really possible as ancillas should always be given in $$\vert 0 \rangle$$). This means that if $$n$$ is sufficiently small, you can simulate the action of your quantum circuit on all the $$2^n$$ possible input states, and verify that the output state is correct (ancillas back to $$\vert 0 \rangle$$, "interesting" qubits in the expected state, ...).

To compute the expected state of your $$n$$ "interesting" qubits, you have 2 ways:

1. Construct the unitary matrix of size $$\left( 2^n, 2^n \right)$$ that represent the expected evolution performed by your quantum circuit and apply it to the input quantum state.
2. Compute "by hand" the expected output quantum state.

For this kind of test, here is the pseudo code.

import numpy

qubit_number = 5
ancillas_number = 3
absolute_error_tol = 1e-3
relative_error_tol = 1e-3

qcirc = get_quantum_circuit(qubit_number, ancillas_number)

# The unitary matrix that your circuit should implement.
# Replace initialisation with your unitary matrix.
reduced_evolution = numpy.zeros((2**qubit_number, 2**qubit_number),
dtype=numpy.complex)

for state in range(2**qubit_number):
# If state = 10 (base 10), do:
# 10 -> 8+2 -> 1010 (base 2) -> |1010> -> vector of size 16 with zeros everywhere
#                                         except in the 10th entry where there is a 1.
qstate = integer_state_to_vector(state)
expected_qstate = reduced_evolution @ qstate # matrix-vector multiplication

computed_qstate = simulate(qcirc, input_state=qstate).get_statevector()
# The compute_qstate is a vector of 2^{qubit_number + ancillas_number} complex values.
# It should be the tensor product between expected_qstate and |0...0> (ancilla qubits
# should be reverted to |0> at the end).
ancilla_state_full_zero = numpy.zeros((2**ancilla_state, ), dtype=numpy.complex)
ancilla_state_full_zero[0] = 1.0

assert numpy.allclose(
numpy.kron(expected_qstate, ancilla_state_full_zero),
computed_qstate,
atol=absolute_error_tol,
rtol=relative_error_tol,
)


Footnote: I think I missed some things, I will come back to this answer as soon as I have more time. Do not hesitate to ask me questions if something is not explained enough.

IBM offers online access to their quantum computer to test quantum programs. Here is the link: https://www.ibm.com/quantum-computing/technology/experience. You can use the portal to check your programs by running them on an actual quantum computer.

• Hello and welcome to Quantum Computing Stack Exchange! Could you expand upon this answer? A link, along with details about how this specifically answers the question, would help this answer be more thorough. Dec 25 '19 at 2:49