Skip to content

Commit 273325d

Browse files
authored
Merge pull request #217 from QuTech-Delft/215-unittest_to_pytest
215 unittest to pytest
2 parents c235abe + 3be6252 commit 273325d

15 files changed

+995
-1153
lines changed
Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,46 @@
1-
import unittest
2-
from test.ir_equality_test_base import IREqualityTestBase
1+
from __future__ import annotations
2+
3+
import math
4+
5+
import pytest
36

47
from opensquirrel.decomposer.cnot_decomposer import CNOTDecomposer
5-
from opensquirrel.default_gates import *
6-
from opensquirrel.ir import Float, Qubit
7-
8-
9-
class CNOTDecomposerTest(IREqualityTestBase):
10-
def test_ignores_1q_gates(self):
11-
self.assertEqual(CNOTDecomposer().decompose(H(Qubit(0))), [H(Qubit(0))])
12-
self.assertEqual(CNOTDecomposer().decompose(Rz(Qubit(0), Float(2.345))), [Rz(Qubit(0), Float(2.345))])
13-
14-
def test_ignores_matrix_gate(self):
15-
self.assertEqual(CNOTDecomposer().decompose(SWAP(Qubit(4), Qubit(3))), [SWAP(Qubit(4), Qubit(3))])
16-
17-
def test_ignores_double_controlled(self):
18-
g = ControlledGate(
19-
control_qubit=Qubit(5), target_gate=ControlledGate(control_qubit=Qubit(2), target_gate=X(Qubit(0)))
20-
)
21-
self.assertEqual(CNOTDecomposer().decompose(g), [g])
22-
23-
def test_CNOT(self):
24-
self.assertEqual(
25-
CNOTDecomposer().decompose(CNOT(Qubit(0), Qubit(1))),
26-
[CNOT(Qubit(0), Qubit(1))],
27-
)
28-
29-
def test_CZ(self):
30-
self.assertEqual(
31-
CNOTDecomposer().decompose(CZ(Qubit(0), Qubit(1))),
32-
[
33-
Rz(Qubit(1), Float(math.pi)),
34-
Ry(Qubit(1), Float(math.pi / 2)),
35-
CNOT(Qubit(0), Qubit(1)),
36-
Ry(Qubit(1), Float(-math.pi / 2)),
37-
Rz(Qubit(1), Float(math.pi)),
38-
],
39-
)
40-
41-
42-
if __name__ == "__main__":
43-
unittest.main()
8+
from opensquirrel.default_gates import CNOT, CZ, SWAP, ControlledGate, H, Ry, Rz, X
9+
from opensquirrel.ir import Float, Gate, Qubit
10+
11+
12+
@pytest.fixture(name="decomposer")
13+
def decomposer_fixture() -> CNOTDecomposer:
14+
return CNOTDecomposer()
15+
16+
17+
@pytest.mark.parametrize(
18+
"gate,expected_result", [(H(Qubit(0)), [H(Qubit(0))]), (Rz(Qubit(0), Float(2.345)), [Rz(Qubit(0), Float(2.345))])]
19+
)
20+
def test_ignores_1q_gates(decomposer: CNOTDecomposer, gate: Gate, expected_result: list[Gate]) -> None:
21+
assert decomposer.decompose(gate) == expected_result
22+
23+
24+
def test_ignores_matrix_gate(decomposer: CNOTDecomposer) -> None:
25+
assert decomposer.decompose(SWAP(Qubit(4), Qubit(3))) == [SWAP(Qubit(4), Qubit(3))]
26+
27+
28+
def test_ignores_double_controlled(decomposer: CNOTDecomposer) -> None:
29+
g = ControlledGate(
30+
control_qubit=Qubit(5), target_gate=ControlledGate(control_qubit=Qubit(2), target_gate=X(Qubit(0)))
31+
)
32+
assert decomposer.decompose(g) == [g]
33+
34+
35+
def test_CNOT(decomposer: CNOTDecomposer) -> None:
36+
assert decomposer.decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))]
37+
38+
39+
def test_CZ(decomposer: CNOTDecomposer) -> None:
40+
assert decomposer.decompose(CZ(Qubit(0), Qubit(1))) == [
41+
Rz(Qubit(1), Float(math.pi)),
42+
Ry(Qubit(1), Float(math.pi / 2)),
43+
CNOT(Qubit(0), Qubit(1)),
44+
Ry(Qubit(1), Float(-math.pi / 2)),
45+
Rz(Qubit(1), Float(math.pi)),
46+
]
Lines changed: 61 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,63 @@
1-
import unittest
2-
from test.ir_equality_test_base import IREqualityTestBase
1+
import math
2+
3+
import pytest
34

45
from opensquirrel.decomposer.mckay_decomposer import McKayDecomposer
5-
from opensquirrel.default_gates import *
6-
from opensquirrel.ir import Float, Qubit
7-
8-
9-
class DecomposeMcKayTests(IREqualityTestBase):
10-
def test_ignores_2q_gates(self):
11-
self.assertEqual(McKayDecomposer().decompose(CNOT(Qubit(0), Qubit(1))), [CNOT(Qubit(0), Qubit(1))])
12-
self.assertEqual(
13-
McKayDecomposer().decompose(CR(Qubit(2), Qubit(3), Float(2.123))), [CR(Qubit(2), Qubit(3), Float(2.123))]
14-
)
15-
16-
def test_identity_empty_decomposition(self):
17-
self.assertEqual(McKayDecomposer().decompose(BlochSphereRotation.identity(Qubit(0))), [])
18-
19-
def test_x(self):
20-
self.assertEqual(
21-
McKayDecomposer().decompose(X(Qubit(0))),
22-
[
23-
# FIXME: we can do better here. See https://github.com/QuTech-Delft/OpenSquirrel/issues/89.
24-
Rz(Qubit(0), Float(-math.pi / 2)),
25-
X90(Qubit(0)),
26-
X90(Qubit(0)),
27-
Rz(Qubit(0), Float(-math.pi / 2)),
28-
],
29-
)
30-
31-
def test_y(self):
32-
self.assertEqual(
33-
McKayDecomposer().decompose(Y(Qubit(0))), [Rz(Qubit(0), Float(math.pi)), X90(Qubit(0)), X90(Qubit(0))]
34-
)
35-
36-
def test_z(self):
37-
self.assertEqual(
38-
McKayDecomposer().decompose(Z(Qubit(0))),
39-
[
40-
Rz(Qubit(0), Float(-math.pi / 2)),
41-
X90(Qubit(0)),
42-
Rz(Qubit(0), Float(math.pi)),
43-
X90(Qubit(0)),
44-
Rz(Qubit(0), Float(math.pi / 2)),
45-
],
46-
)
47-
48-
def test_hadamard(self):
49-
self.assertEqual(
50-
McKayDecomposer().decompose(H(Qubit(0))),
51-
[
52-
BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0),
53-
BlochSphereRotation(Qubit(0), axis=(0, 0, 1), angle=math.pi / 2, phase=0.0),
54-
BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0),
55-
],
56-
)
57-
58-
def test_arbitrary(self):
59-
self.assertEqual(
60-
McKayDecomposer().decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)),
61-
[
62-
Rz(Qubit(0), Float(0.018644578210707863)),
63-
X90(Qubit(0)),
64-
Rz(Qubit(0), Float(2.520651583905213)),
65-
X90(Qubit(0)),
66-
Rz(Qubit(0), Float(2.2329420137988887)),
67-
],
68-
)
69-
70-
71-
if __name__ == "__main__":
72-
unittest.main()
6+
from opensquirrel.default_gates import CNOT, CR, X90, H, Rz, X, Y, Z
7+
from opensquirrel.ir import BlochSphereRotation, Float, Qubit
8+
9+
10+
@pytest.fixture(name="decomposer")
11+
def decomposer_fixture() -> McKayDecomposer:
12+
return McKayDecomposer()
13+
14+
15+
def test_ignores_2q_gates(decomposer: McKayDecomposer) -> None:
16+
assert decomposer.decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))]
17+
assert decomposer.decompose(CR(Qubit(2), Qubit(3), Float(2.123))) == [CR(Qubit(2), Qubit(3), Float(2.123))]
18+
19+
20+
def test_identity_empty_decomposition(decomposer: McKayDecomposer) -> None:
21+
assert decomposer.decompose(BlochSphereRotation.identity(Qubit(0))) == []
22+
23+
24+
def test_x(decomposer: McKayDecomposer) -> None:
25+
assert decomposer.decompose(X(Qubit(0))) == [
26+
# FIXME: we can do better here. See https://github.com/QuTech-Delft/OpenSquirrel/issues/89.
27+
Rz(Qubit(0), Float(-math.pi / 2)),
28+
X90(Qubit(0)),
29+
X90(Qubit(0)),
30+
Rz(Qubit(0), Float(-math.pi / 2)),
31+
]
32+
33+
34+
def test_y(decomposer: McKayDecomposer) -> None:
35+
assert decomposer.decompose(Y(Qubit(0))) == [Rz(Qubit(0), Float(math.pi)), X90(Qubit(0)), X90(Qubit(0))]
36+
37+
38+
def test_z(decomposer: McKayDecomposer) -> None:
39+
assert decomposer.decompose(Z(Qubit(0))) == [
40+
Rz(Qubit(0), Float(-math.pi / 2)),
41+
X90(Qubit(0)),
42+
Rz(Qubit(0), Float(math.pi)),
43+
X90(Qubit(0)),
44+
Rz(Qubit(0), Float(math.pi / 2)),
45+
]
46+
47+
48+
def test_hadamard(decomposer: McKayDecomposer) -> None:
49+
assert decomposer.decompose(H(Qubit(0))) == [
50+
BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0),
51+
BlochSphereRotation(Qubit(0), axis=(0, 0, 1), angle=math.pi / 2, phase=0.0),
52+
BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0),
53+
]
54+
55+
56+
def test_arbitrary(decomposer: McKayDecomposer) -> None:
57+
assert decomposer.decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)) == [
58+
Rz(Qubit(0), Float(0.018644578210707863)),
59+
X90(Qubit(0)),
60+
Rz(Qubit(0), Float(2.520651583905213)),
61+
X90(Qubit(0)),
62+
Rz(Qubit(0), Float(2.2329420137988887)),
63+
]

test/exporter/test_quantify_scheduler_exporter.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import contextlib
22
import importlib.util
33
import math
4-
import unittest
54
import unittest.mock
65

6+
import pytest
7+
78
from opensquirrel.circuit import Circuit
89
from opensquirrel.common import ATOL
910
from opensquirrel.default_gates import CCZ, CZ, SWAP, H, Ry, Rz, X
@@ -38,7 +39,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback):
3839
self._stack.__exit__(exc_type, exc_value, exc_traceback)
3940

4041

41-
class QuantifySchedulerExporterTest(unittest.TestCase):
42+
class TestQuantifySchedulerExporter:
4243
def test_export(self):
4344
register_manager = RegisterManager(qubit_register_size=3)
4445
ir = IR()
@@ -68,15 +69,15 @@ def test_export(self):
6869
)
6970
mock_quantify_scheduler_gates.CZ.assert_called_once_with(qC="q[0]", qT="q[1]")
7071
mock_quantify_scheduler_gates.Rz.assert_called_once_with(theta=FloatEq(math.degrees(2.34)), qubit="q[1]")
71-
self.assertEqual(mock_schedule.add.call_count, 7)
72+
assert mock_schedule.add.call_count == 7
7273

73-
def check_gate_not_supported(self, g: Gate):
74+
def check_gate_not_supported(self, g: Gate) -> None:
7475
register_manager = RegisterManager(qubit_register_size=3)
7576
ir = IR()
7677
ir.add_gate(g)
7778

7879
with MockedQuantifyScheduler():
79-
with self.assertRaisesRegex(Exception, "Cannot exporter circuit: it contains unsupported gates"):
80+
with pytest.raises(Exception, match="Cannot exporter circuit: it contains unsupported gates"):
8081
quantify_scheduler_exporter.export(Circuit(register_manager, ir))
8182

8283
def test_gates_not_supported(self):
@@ -86,12 +87,10 @@ def test_gates_not_supported(self):
8687
self.check_gate_not_supported(CCZ(Qubit(0), Qubit(1), Qubit(2)))
8788

8889

89-
class QuantifySchedulerNotInstalledTest(unittest.TestCase):
90-
@unittest.skipIf(
91-
importlib.util.find_spec("quantify_scheduler") is not None, reason="quantify_scheduler is installed"
92-
)
93-
def test_quantify_scheduler_not_installed(self):
94-
with self.assertRaisesRegex(
95-
Exception, "quantify-scheduler is not installed, or cannot be installed on your system"
96-
):
97-
quantify_scheduler_exporter.export(unittest.mock.MagicMock())
90+
@pytest.mark.skipif(
91+
importlib.util.find_spec("quantify_scheduler") is not None, reason="quantify_scheduler is installed"
92+
)
93+
def test_quantify_scheduler_not_installed() -> None:
94+
empty_circuit = Circuit(RegisterManager(1), IR())
95+
with pytest.raises(Exception, match="quantify-scheduler is not installed, or cannot be installed on your system"):
96+
quantify_scheduler_exporter.export(empty_circuit)

test/ir_equality_test_base.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,37 @@
1-
import unittest
1+
from __future__ import annotations
22

3-
from opensquirrel import circuit_matrix_calculator
4-
from opensquirrel.circuit import Circuit
3+
from collections.abc import Callable
4+
from typing import Any
5+
6+
import numpy as np
7+
from numpy.typing import NDArray
8+
9+
from opensquirrel import Circuit, circuit_matrix_calculator
510
from opensquirrel.common import are_matrices_equivalent_up_to_global_phase
611

712

8-
class IREqualityTestBase(unittest.TestCase):
9-
def check_equivalence_up_to_global_phase(self, matrix_a, matrix_b):
10-
self.assertTrue(are_matrices_equivalent_up_to_global_phase(matrix_a, matrix_b))
13+
def check_equivalence_up_to_global_phase(matrix_a: NDArray[np.complex_], matrix_b: NDArray[np.complex_]) -> None:
14+
assert are_matrices_equivalent_up_to_global_phase(matrix_a, matrix_b)
15+
1116

12-
def modify_circuit_and_check(self, circuit, action, expected_circuit=None):
13-
"""
14-
Checks whether the action preserves:
15-
- the number of qubits,
16-
- the qubit register name(s),
17-
- the circuit matrix up to a global phase factor.
18-
"""
19-
# Store matrix before decompositions.
20-
expected_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit)
17+
def modify_circuit_and_check(
18+
circuit: Circuit, action: Callable[[Circuit, Any]], expected_circuit: Circuit | None = None
19+
):
20+
"""
21+
Checks whether the action preserves:
22+
- the number of qubits,
23+
- the qubit register name(s),
24+
- the circuit matrix up to a global phase factor.
25+
"""
26+
# Store matrix before decompositions.
27+
expected_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit)
2128

22-
action(circuit)
29+
action(circuit)
2330

24-
# Get matrix after decompositions.
25-
actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit)
31+
# Get matrix after decompositions.
32+
actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit)
2633

27-
self.check_equivalence_up_to_global_phase(actual_matrix, expected_matrix)
34+
check_equivalence_up_to_global_phase(actual_matrix, expected_matrix)
2835

29-
if expected_circuit is not None:
30-
self.assertEqual(circuit, expected_circuit)
36+
if expected_circuit is not None:
37+
assert circuit == expected_circuit

test/mapper/test_qubit_remapper.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from typing import List
2-
31
import pytest
42

53
from opensquirrel.circuit import Circuit

0 commit comments

Comments
 (0)