Skip to content

Commit

Permalink
Add tests to the library (#130)
Browse files Browse the repository at this point in the history
Add a few automatic tests to the library. More should be added in the future.
  • Loading branch information
nelimee authored Mar 1, 2024
1 parent 2b6d313 commit a3ef9db
Show file tree
Hide file tree
Showing 21 changed files with 913 additions and 179 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Repository = "https://github.com/QCHackers/tqec"
Issues = "https://github.com/QCHackers/tqec/issues"

[project.optional-dependencies]
test = ["pytest", "mypy"]
test = ["pytest", "mypy", "pytest-cov"]
dev = ["sinter", "pymatching", "jupyterlab", "tqec[test]"]
all = ["tqec[test, dev]"]

Expand Down
2 changes: 1 addition & 1 deletion src/tqec/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import importlib
import importlib.metadata

__version__ = importlib.metadata.version("tqec")
13 changes: 10 additions & 3 deletions src/tqec/circuit/circuit.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import typing as ty
from copy import deepcopy

import cirq
Expand Down Expand Up @@ -34,22 +35,28 @@ def generate_circuit(
we want to implement.
plaquettes: description of the computation that should happen at
different time-slices of the quantum error correction experiment (or
at least part of it).
at least part of it). If provided as a dictionary, plaquettes should be
1-indexed (i.e., ``0 not in plaquettes`` should be ``True``).
Returns:
a cirq.Circuit instance implementing the (part of) quantum error
correction experiment represented by the provided inputs.
Raises:
TQECException: if the provided plaquettes do not match the expected
number of plaquettes for the given template.
TQECException: if ``len(plaquettes) != template.expected_plaquettes_number`` or
if plaquettes is provided as a dicitonary and ``0 in plaquettes``.
"""
# Check that the user gave enough plaquettes.
if len(plaquettes) != template.expected_plaquettes_number:
raise TQECException(
f"{len(plaquettes)} plaquettes have been provided, but "
f"{template.expected_plaquettes_number} were expected."
)
if isinstance(plaquettes, ty.Mapping) and 0 in plaquettes:
raise TQECException(
"If using a dictionary, the input plaquettes parameter should not "
f"contain the entry 0. Found a value ({plaquettes[0]}) at entry 0."
)

# If plaquettes are given as a list, make that a dict to simplify the following operations
if isinstance(plaquettes, list):
Expand Down
208 changes: 41 additions & 167 deletions src/tqec/circuit/circuit_test.py
Original file line number Diff line number Diff line change
@@ -1,181 +1,55 @@
"""Example taken from /notebooks/logical_qubit_memory_experiment.ipynb"""

import cirq
import pytest

from tqec.circuit.circuit import generate_circuit
from tqec.circuit.operations.operation import make_shift_coords
from tqec.enums import PlaquetteOrientation
from tqec.plaquette.library import (
MeasurementRoundedPlaquette,
MeasurementSquarePlaquette,
XXMemoryPlaquette,
XXXXMemoryPlaquette,
ZRoundedInitialisationPlaquette,
ZSquareInitialisationPlaquette,
ZZMemoryPlaquette,
ZZZZMemoryPlaquette,
)
from tqec.exceptions import TQECException
from tqec.plaquette.library import ZSquareInitialisationPlaquette
from tqec.plaquette.plaquette import Plaquette
from tqec.templates.constructions.qubit import QubitSquareTemplate
from tqec.templates.scale import Dimension, LinearFunction
from tqec.templates.atomic.rectangle import RawRectangleTemplate
from tqec.templates.base import Template


def _normalise_circuit(norm_circuit: cirq.Circuit) -> cirq.Circuit:
ordered_transformers = [
cirq.drop_empty_moments,
]
for transformer in ordered_transformers:
norm_circuit = transformer(norm_circuit)
return norm_circuit
@pytest.fixture
def initialisation_plaquette() -> Plaquette:
return ZSquareInitialisationPlaquette()


def _make_repeated_layer(repeat_circuit: cirq.Circuit) -> cirq.Circuit:
# Note: we do not care on which qubit it is applied, but we want a SHIFT_COORDS instruction
# to be inserted somewhere in the repetition loop. It is inserted at the beginning.
any_qubit = next(iter(repeat_circuit.all_qubits()), None)
assert (
any_qubit is not None
), "Could not find any qubit in the given Circuit instance."
circuit_to_repeat = cirq.Circuit([make_shift_coords(0, 0, 1)]) + repeat_circuit
repeated_circuit_operation = cirq.CircuitOperation(
circuit_to_repeat.freeze()
).repeat(9)
return cirq.Circuit([repeated_circuit_operation])
@pytest.fixture
def one_by_one_template() -> Template:
return RawRectangleTemplate([[0]])


def _generate_circuit() -> cirq.Circuit:
template = QubitSquareTemplate(Dimension(2, LinearFunction(2)))
plaquettes: list[list[Plaquette]] = [
[
ZRoundedInitialisationPlaquette(PlaquetteOrientation.UP),
XXMemoryPlaquette(
PlaquetteOrientation.UP,
[1, 2, 5, 6, 7, 8],
include_detector=False,
is_first_round=True,
),
XXMemoryPlaquette(PlaquetteOrientation.UP, [1, 2, 5, 6, 7, 8]),
MeasurementRoundedPlaquette(
PlaquetteOrientation.UP, include_detector=False
),
],
[
ZRoundedInitialisationPlaquette(PlaquetteOrientation.LEFT),
ZZMemoryPlaquette(
PlaquetteOrientation.LEFT, [1, 5, 6, 8], is_first_round=True
),
ZZMemoryPlaquette(PlaquetteOrientation.LEFT, [1, 5, 6, 8]),
MeasurementRoundedPlaquette(PlaquetteOrientation.LEFT),
],
[
ZSquareInitialisationPlaquette(),
XXXXMemoryPlaquette(
[1, 2, 3, 4, 5, 6, 7, 8], include_detector=False, is_first_round=True
),
XXXXMemoryPlaquette([1, 2, 3, 4, 5, 6, 7, 8]),
MeasurementSquarePlaquette(include_detector=False),
],
[
ZSquareInitialisationPlaquette(),
ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8], is_first_round=True),
ZZZZMemoryPlaquette([1, 3, 4, 5, 6, 8]),
MeasurementSquarePlaquette(),
],
[
ZRoundedInitialisationPlaquette(PlaquetteOrientation.RIGHT),
ZZMemoryPlaquette(
PlaquetteOrientation.RIGHT, [1, 3, 4, 8], is_first_round=True
),
ZZMemoryPlaquette(PlaquetteOrientation.RIGHT, [1, 3, 4, 8]),
MeasurementRoundedPlaquette(PlaquetteOrientation.RIGHT),
],
[
ZRoundedInitialisationPlaquette(PlaquetteOrientation.DOWN),
XXMemoryPlaquette(
PlaquetteOrientation.DOWN,
[1, 2, 3, 4, 7, 8],
include_detector=False,
is_first_round=True,
),
XXMemoryPlaquette(PlaquetteOrientation.DOWN, [1, 2, 3, 4, 7, 8]),
MeasurementRoundedPlaquette(
PlaquetteOrientation.DOWN, include_detector=False
),
],
]
layer_modificators = {1: _make_repeated_layer}
def test_generate_initialisation_circuit_list(
initialisation_plaquette: Plaquette, one_by_one_template
):
circuit = generate_circuit(one_by_one_template, [initialisation_plaquette])
assert circuit == cirq.Circuit(
cirq.R(q.to_grid_qubit()) for q in initialisation_plaquette.qubits
)

# 4. Actually create the cirq.Circuit instance by concatenating the circuits generated
# for each layers and potentially modified by the modifiers defined above.
circuit = cirq.Circuit()
for layer_index in range(4):
layer_circuit = generate_circuit(
template,
[plaquette_list[layer_index] for plaquette_list in plaquettes],
)
layer_circuit = _normalise_circuit(layer_circuit)
circuit += layer_modificators.get(layer_index, lambda circ: circ)(layer_circuit)

return circuit
def test_generate_initialisation_circuit_dict(
initialisation_plaquette: Plaquette, one_by_one_template
):
circuit = generate_circuit(one_by_one_template, {1: initialisation_plaquette})
assert circuit == cirq.Circuit(
cirq.R(q.to_grid_qubit()) for q in initialisation_plaquette.qubits
)


def test_generate_initialisation_circuit_dict_0_indexed(
initialisation_plaquette: Plaquette, one_by_one_template
):
with pytest.raises(TQECException):
generate_circuit(one_by_one_template, {0: initialisation_plaquette})

def test_generate_circuit():
"""Minimal test to check that the circuit is generated correctly
The target qubits are taken from the orginal notebook output.
"""
generated_circuit = _generate_circuit()
generate_qubits = [(q.row, q.col) for q in generated_circuit.all_qubits()]
target_qubits = [
(6, 2),
(3, 7),
(3, 9),
(1, 1),
(10, 6),
(8, 8),
(1, 5),
(2, 0),
(6, 4),
(2, 2),
(6, 6),
(1, 3),
(7, 1),
(7, 3),
(4, 2),
(1, 7),
(7, 5),
(1, 9),
(7, 7),
(2, 4),
(6, 8),
(9, 1),
(2, 6),
(9, 3),
(9, 7),
(4, 6),
(4, 4),
(7, 9),
(5, 1),
(5, 3),
(2, 8),
(9, 5),
(5, 7),
(8, 2),
(0, 4),
(9, 9),
(3, 1),
(4, 8),
(5, 5),
(3, 3),
(4, 10),
(3, 5),
(10, 2),
(8, 4),
(5, 9),
(8, 6),
(0, 8),
(6, 0),
(8, 10),
]
generate_qubits.sort()
target_qubits.sort()
assert generate_qubits == target_qubits

def test_generate_circuit_wrong_number_of_plaquettes(
initialisation_plaquette: Plaquette, one_by_one_template
):
with pytest.raises(TQECException):
generate_circuit(
one_by_one_template, [initialisation_plaquette, initialisation_plaquette]
)
with pytest.raises(TQECException):
generate_circuit(one_by_one_template, [])
Loading

0 comments on commit a3ef9db

Please sign in to comment.