From 932cbef607dbed5d3f24681e86ddb923a276155d Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:00:07 +0200 Subject: [PATCH 01/14] Update tests decomposer --- test/decomposer/test_cnot_decomposer.py | 85 ++++++++------- test/decomposer/test_mckay_decomposer.py | 131 +++++++++++------------ 2 files changed, 105 insertions(+), 111 deletions(-) diff --git a/test/decomposer/test_cnot_decomposer.py b/test/decomposer/test_cnot_decomposer.py index 4ab6620d..a12ed0c3 100644 --- a/test/decomposer/test_cnot_decomposer.py +++ b/test/decomposer/test_cnot_decomposer.py @@ -1,43 +1,46 @@ -import unittest -from test.ir_equality_test_base import IREqualityTestBase +from __future__ import annotations + +import math + +import pytest from opensquirrel.decomposer.cnot_decomposer import CNOTDecomposer -from opensquirrel.default_gates import * -from opensquirrel.ir import Float, Qubit - - -class CNOTDecomposerTest(IREqualityTestBase): - def test_ignores_1q_gates(self): - self.assertEqual(CNOTDecomposer().decompose(H(Qubit(0))), [H(Qubit(0))]) - self.assertEqual(CNOTDecomposer().decompose(Rz(Qubit(0), Float(2.345))), [Rz(Qubit(0), Float(2.345))]) - - def test_ignores_matrix_gate(self): - self.assertEqual(CNOTDecomposer().decompose(SWAP(Qubit(4), Qubit(3))), [SWAP(Qubit(4), Qubit(3))]) - - def test_ignores_double_controlled(self): - g = ControlledGate( - control_qubit=Qubit(5), target_gate=ControlledGate(control_qubit=Qubit(2), target_gate=X(Qubit(0))) - ) - self.assertEqual(CNOTDecomposer().decompose(g), [g]) - - def test_CNOT(self): - self.assertEqual( - CNOTDecomposer().decompose(CNOT(Qubit(0), Qubit(1))), - [CNOT(Qubit(0), Qubit(1))], - ) - - def test_CZ(self): - self.assertEqual( - CNOTDecomposer().decompose(CZ(Qubit(0), Qubit(1))), - [ - Rz(Qubit(1), Float(math.pi)), - Ry(Qubit(1), Float(math.pi / 2)), - CNOT(Qubit(0), Qubit(1)), - Ry(Qubit(1), Float(-math.pi / 2)), - Rz(Qubit(1), Float(math.pi)), - ], - ) - - -if __name__ == "__main__": - unittest.main() +from opensquirrel.default_gates import CNOT, CZ, SWAP, ControlledGate, H, Ry, Rz, X +from opensquirrel.ir import Float, Gate, Qubit + + +@pytest.fixture(name="decomposer") +def decomposer_fixture() -> CNOTDecomposer: + return CNOTDecomposer() + + +@pytest.mark.parametrize( + "gate,expected_result", [(H(Qubit(0)), [H(Qubit(0))]), (Rz(Qubit(0), Float(2.345)), [Rz(Qubit(0), Float(2.345))])] +) +def test_ignores_1q_gates(decomposer: CNOTDecomposer, gate: Gate, expected_result: list[Gate]) -> None: + assert decomposer.decompose(gate) == expected_result + + +def test_ignores_matrix_gate(decomposer: CNOTDecomposer) -> None: + assert decomposer.decompose(SWAP(Qubit(4), Qubit(3))) == [SWAP(Qubit(4), Qubit(3))] + + +def test_ignores_double_controlled(decomposer: CNOTDecomposer) -> None: + g = ControlledGate( + control_qubit=Qubit(5), target_gate=ControlledGate(control_qubit=Qubit(2), target_gate=X(Qubit(0))) + ) + assert decomposer.decompose(g) == [g] + + +def test_CNOT(decomposer: CNOTDecomposer) -> None: + assert decomposer.decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))] + + +def test_CZ(decomposer: CNOTDecomposer) -> None: + assert decomposer.decompose(CZ(Qubit(0), Qubit(1))) == [ + Rz(Qubit(1), Float(math.pi)), + Ry(Qubit(1), Float(math.pi / 2)), + CNOT(Qubit(0), Qubit(1)), + Ry(Qubit(1), Float(-math.pi / 2)), + Rz(Qubit(1), Float(math.pi)), + ] diff --git a/test/decomposer/test_mckay_decomposer.py b/test/decomposer/test_mckay_decomposer.py index 04ea40bd..a6e56413 100644 --- a/test/decomposer/test_mckay_decomposer.py +++ b/test/decomposer/test_mckay_decomposer.py @@ -1,72 +1,63 @@ -import unittest -from test.ir_equality_test_base import IREqualityTestBase +import math + +import pytest from opensquirrel.decomposer.mckay_decomposer import McKayDecomposer -from opensquirrel.default_gates import * -from opensquirrel.ir import Float, Qubit - - -class DecomposeMcKayTests(IREqualityTestBase): - def test_ignores_2q_gates(self): - self.assertEqual(McKayDecomposer().decompose(CNOT(Qubit(0), Qubit(1))), [CNOT(Qubit(0), Qubit(1))]) - self.assertEqual( - McKayDecomposer().decompose(CR(Qubit(2), Qubit(3), Float(2.123))), [CR(Qubit(2), Qubit(3), Float(2.123))] - ) - - def test_identity_empty_decomposition(self): - self.assertEqual(McKayDecomposer().decompose(BlochSphereRotation.identity(Qubit(0))), []) - - def test_x(self): - self.assertEqual( - McKayDecomposer().decompose(X(Qubit(0))), - [ - # FIXME: we can do better here. See https://github.com/QuTech-Delft/OpenSquirrel/issues/89. - Rz(Qubit(0), Float(-math.pi / 2)), - X90(Qubit(0)), - X90(Qubit(0)), - Rz(Qubit(0), Float(-math.pi / 2)), - ], - ) - - def test_y(self): - self.assertEqual( - McKayDecomposer().decompose(Y(Qubit(0))), [Rz(Qubit(0), Float(math.pi)), X90(Qubit(0)), X90(Qubit(0))] - ) - - def test_z(self): - self.assertEqual( - McKayDecomposer().decompose(Z(Qubit(0))), - [ - Rz(Qubit(0), Float(-math.pi / 2)), - X90(Qubit(0)), - Rz(Qubit(0), Float(math.pi)), - X90(Qubit(0)), - Rz(Qubit(0), Float(math.pi / 2)), - ], - ) - - def test_hadamard(self): - self.assertEqual( - McKayDecomposer().decompose(H(Qubit(0))), - [ - BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0), - BlochSphereRotation(Qubit(0), axis=(0, 0, 1), angle=math.pi / 2, phase=0.0), - BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0), - ], - ) - - def test_arbitrary(self): - self.assertEqual( - McKayDecomposer().decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)), - [ - Rz(Qubit(0), Float(0.018644578210707863)), - X90(Qubit(0)), - Rz(Qubit(0), Float(2.520651583905213)), - X90(Qubit(0)), - Rz(Qubit(0), Float(2.2329420137988887)), - ], - ) - - -if __name__ == "__main__": - unittest.main() +from opensquirrel.default_gates import CNOT, CR, X90, H, Rz, X, Y, Z +from opensquirrel.ir import BlochSphereRotation, Float, Qubit + + +@pytest.fixture(name="decomposer") +def decomposer_fixture() -> McKayDecomposer: + return McKayDecomposer() + + +def test_ignores_2q_gates(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))] + assert decomposer.decompose(CR(Qubit(2), Qubit(3), Float(2.123))) == [CR(Qubit(2), Qubit(3), Float(2.123))] + + +def test_identity_empty_decomposition(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(BlochSphereRotation.identity(Qubit(0))) == [] + + +def test_x(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(X(Qubit(0))) == [ + # FIXME: we can do better here. See https://github.com/QuTech-Delft/OpenSquirrel/issues/89. + Rz(Qubit(0), Float(-math.pi / 2)), + X90(Qubit(0)), + X90(Qubit(0)), + Rz(Qubit(0), Float(-math.pi / 2)), + ] + + +def test_y(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(Y(Qubit(0))) == [Rz(Qubit(0), Float(math.pi)), X90(Qubit(0)), X90(Qubit(0))] + + +def test_z(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(Z(Qubit(0))) == [ + Rz(Qubit(0), Float(-math.pi / 2)), + X90(Qubit(0)), + Rz(Qubit(0), Float(math.pi)), + X90(Qubit(0)), + Rz(Qubit(0), Float(math.pi / 2)), + ] + + +def test_hadamard(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(H(Qubit(0))) == [ + BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0), + BlochSphereRotation(Qubit(0), axis=(0, 0, 1), angle=math.pi / 2, phase=0.0), + BlochSphereRotation(Qubit(0), axis=(1, 0, 0), angle=math.pi / 2, phase=0.0), + ] + + +def test_arbitrary(decomposer: McKayDecomposer) -> None: + assert decomposer.decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)) == [ + Rz(Qubit(0), Float(0.018644578210707863)), + X90(Qubit(0)), + Rz(Qubit(0), Float(2.520651583905213)), + X90(Qubit(0)), + Rz(Qubit(0), Float(2.2329420137988887)), + ] From f8326465961926f5ca4f0d07fd417a3c829d9928 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:00:22 +0200 Subject: [PATCH 02/14] Update tests mapper --- test/mapper/test_qubit_remapper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/mapper/test_qubit_remapper.py b/test/mapper/test_qubit_remapper.py index 75381c6d..10b8d189 100644 --- a/test/mapper/test_qubit_remapper.py +++ b/test/mapper/test_qubit_remapper.py @@ -1,5 +1,3 @@ -from typing import List - import pytest from opensquirrel.circuit import Circuit From f3b04a155dee28164cdef10bc9d56f05d49ce012 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:00:35 +0200 Subject: [PATCH 03/14] Update tests merger --- test/merger/test_merger.py | 235 +++++++++++++++++++------------------ 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/test/merger/test_merger.py b/test/merger/test_merger.py index 23c19eef..2a0796e9 100644 --- a/test/merger/test_merger.py +++ b/test/merger/test_merger.py @@ -1,124 +1,125 @@ -import unittest -from test.ir_equality_test_base import IREqualityTestBase +import math +from test.ir_equality_test_base import modify_circuit_and_check from opensquirrel.circuit import Circuit -from opensquirrel.default_gates import * -from opensquirrel.ir import IR, Float, Qubit +from opensquirrel.default_gates import CNOT, H, Rx, Ry, Rz +from opensquirrel.ir import IR, BlochSphereRotation, Float, Qubit from opensquirrel.merger import general_merger from opensquirrel.merger.general_merger import compose_bloch_sphere_rotations from opensquirrel.register_manager import RegisterManager -class MergerTest(IREqualityTestBase): - def test_compose_bloch_sphere_rotations_same_axis(self): - q = Qubit(123) - a = BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=0.4) - b = BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=-0.3) - composed = compose_bloch_sphere_rotations(a, b) - self.assertEqual(composed, BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=0.1)) - - def test_compose_bloch_sphere_rotations_different_axis(self): - # Visualizing this in 3D is difficult... - q = Qubit(123) - a = BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi / 2) - b = BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 2) - c = BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi / 2) - composed = compose_bloch_sphere_rotations(a, compose_bloch_sphere_rotations(b, c)) - self.assertEqual(composed, BlochSphereRotation(qubit=q, axis=(1, 1, 0), angle=math.pi)) - - def test_single_gate(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(Ry(Qubit(0), Float(1.2345))) - circuit = Circuit(register_manager, ir) - - expected_ir = IR() - expected_ir.add_gate(Ry(Qubit(0), Float(1.2345))) - expected_circuit = Circuit(register_manager, expected_ir) - - self.modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) - - # Check that when no fusion happens, generator and arguments of gates are preserved. - self.assertEqual(ir.statements[0].generator, Ry) - self.assertEqual(ir.statements[0].arguments, (Qubit(0), Float(1.2345))) - - def test_two_hadamards(self): - register_manager = RegisterManager(qubit_register_size=4) - ir = IR() - ir.add_gate(H(Qubit(2))) - ir.add_gate(H(Qubit(2))) - circuit = Circuit(register_manager, ir) - - expected_ir = IR() - expected_circuit = Circuit(register_manager, expected_ir) - - self.modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) - - def test_two_hadamards_different_qubits(self): - register_manager = RegisterManager(qubit_register_size=4) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(H(Qubit(2))) - circuit = Circuit(register_manager, ir) - - expected_ir = IR() - expected_ir.add_gate(H(Qubit(0))) - expected_ir.add_gate(H(Qubit(2))) - expected_circuit = Circuit(register_manager, expected_ir) - - self.modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) - - def test_merge_different_qubits(self): - register_manager = RegisterManager(qubit_register_size=4) - ir = IR() - ir.add_gate(Ry(Qubit(0), Float(math.pi / 2))) - ir.add_gate(Rx(Qubit(0), Float(math.pi))) - ir.add_gate(Rz(Qubit(1), Float(1.2345))) - ir.add_gate(Ry(Qubit(2), Float(1))) - ir.add_gate(Ry(Qubit(2), Float(3.234))) - circuit = Circuit(register_manager, ir) - - expected_ir = IR() - expected_ir.add_gate( - BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 1), angle=math.pi) - ) # This is hadamard with 0 phase... - expected_ir.add_gate(Rz(Qubit(1), Float(1.2345))) - expected_ir.add_gate(Ry(Qubit(2), Float(4.234))) - expected_circuit = Circuit(register_manager, expected_ir) - - self.modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) - - self.assertTrue(ir.statements[0].is_anonymous) # When fusion happens, the resulting gate is anonymous. - self.assertEqual(ir.statements[1].generator, Rz) # Otherwise it keeps the same generator and arguments. - self.assertEqual(ir.statements[1].arguments, (Qubit(1), Float(1.2345))) - self.assertTrue(ir.statements[2].is_anonymous) - - def test_merge_and_flush(self): - register_manager = RegisterManager(qubit_register_size=4) - ir = IR() - ir.add_gate(Ry(Qubit(0), Float(math.pi / 2))) - ir.add_gate(Rz(Qubit(1), Float(1.5))) - ir.add_gate(Rx(Qubit(0), Float(math.pi))) - ir.add_gate(Rz(Qubit(1), Float(-2.5))) - ir.add_gate(CNOT(Qubit(0), Qubit(1))) - ir.add_gate(Ry(Qubit(0), Float(3.234))) - circuit = Circuit(register_manager, ir) - - expected_ir = IR() - expected_ir.add_gate( - BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 1), angle=math.pi) - ) # This is hadamard with 0 phase... - expected_ir.add_gate(Rz(Qubit(1), Float(-1.0))) - expected_ir.add_gate(CNOT(Qubit(0), Qubit(1))) - expected_ir.add_gate(Ry(Qubit(0), Float(3.234))) - expected_circuit = Circuit(register_manager, expected_ir) - - self.modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) - - self.assertTrue(ir.statements[0].is_anonymous) - self.assertEqual(ir.statements[3].generator, Ry) - self.assertEqual(ir.statements[3].arguments, (Qubit(0), Float(3.234))) - - -if __name__ == "__main__": - unittest.main() +def test_compose_bloch_sphere_rotations_same_axis() -> None: + q = Qubit(123) + a = BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=0.4) + b = BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=-0.3) + composed = compose_bloch_sphere_rotations(a, b) + assert composed == BlochSphereRotation(qubit=q, axis=(1, 2, 3), angle=0.1) + + +def test_compose_bloch_sphere_rotations_different_axis() -> None: + # Visualizing this in 3D is difficult... + q = Qubit(123) + a = BlochSphereRotation(qubit=q, axis=(1, 0, 0), angle=math.pi / 2) + b = BlochSphereRotation(qubit=q, axis=(0, 0, 1), angle=-math.pi / 2) + c = BlochSphereRotation(qubit=q, axis=(0, 1, 0), angle=math.pi / 2) + composed = compose_bloch_sphere_rotations(a, compose_bloch_sphere_rotations(b, c)) + assert composed == BlochSphereRotation(qubit=q, axis=(1, 1, 0), angle=math.pi) + + +def test_single_gate() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(Ry(Qubit(0), Float(1.2345))) + circuit = Circuit(register_manager, ir) + + expected_ir = IR() + expected_ir.add_gate(Ry(Qubit(0), Float(1.2345))) + expected_circuit = Circuit(register_manager, expected_ir) + + modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) + + # Check that when no fusion happens, generator and arguments of gates are preserved. + assert ir.statements[0].generator == Ry + assert ir.statements[0].arguments == (Qubit(0), Float(1.2345)) + + +def test_two_hadamards() -> None: + register_manager = RegisterManager(qubit_register_size=4) + ir = IR() + ir.add_gate(H(Qubit(2))) + ir.add_gate(H(Qubit(2))) + circuit = Circuit(register_manager, ir) + + expected_ir = IR() + expected_circuit = Circuit(register_manager, expected_ir) + + modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) + + +def test_two_hadamards_different_qubits() -> None: + register_manager = RegisterManager(qubit_register_size=4) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(H(Qubit(2))) + circuit = Circuit(register_manager, ir) + + expected_ir = IR() + expected_ir.add_gate(H(Qubit(0))) + expected_ir.add_gate(H(Qubit(2))) + expected_circuit = Circuit(register_manager, expected_ir) + + modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) + + +def test_merge_different_qubits() -> None: + register_manager = RegisterManager(qubit_register_size=4) + ir = IR() + ir.add_gate(Ry(Qubit(0), Float(math.pi / 2))) + ir.add_gate(Rx(Qubit(0), Float(math.pi))) + ir.add_gate(Rz(Qubit(1), Float(1.2345))) + ir.add_gate(Ry(Qubit(2), Float(1))) + ir.add_gate(Ry(Qubit(2), Float(3.234))) + circuit = Circuit(register_manager, ir) + + expected_ir = IR() + expected_ir.add_gate( + BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 1), angle=math.pi) + ) # This is hadamard with 0 phase... + expected_ir.add_gate(Rz(Qubit(1), Float(1.2345))) + expected_ir.add_gate(Ry(Qubit(2), Float(4.234))) + expected_circuit = Circuit(register_manager, expected_ir) + + modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) + + assert ir.statements[0].is_anonymous # When fusion happens, the resulting gate is anonymous. + assert ir.statements[1].generator == Rz # Otherwise it keeps the same generator and arguments. + assert ir.statements[1].arguments == (Qubit(1), Float(1.2345)) + assert ir.statements[2].is_anonymous + + +def test_merge_and_flush() -> None: + register_manager = RegisterManager(qubit_register_size=4) + ir = IR() + ir.add_gate(Ry(Qubit(0), Float(math.pi / 2))) + ir.add_gate(Rz(Qubit(1), Float(1.5))) + ir.add_gate(Rx(Qubit(0), Float(math.pi))) + ir.add_gate(Rz(Qubit(1), Float(-2.5))) + ir.add_gate(CNOT(Qubit(0), Qubit(1))) + ir.add_gate(Ry(Qubit(0), Float(3.234))) + circuit = Circuit(register_manager, ir) + + expected_ir = IR() + expected_ir.add_gate( + BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 1), angle=math.pi) + ) # This is hadamard with 0 phase... + expected_ir.add_gate(Rz(Qubit(1), Float(-1.0))) + expected_ir.add_gate(CNOT(Qubit(0), Qubit(1))) + expected_ir.add_gate(Ry(Qubit(0), Float(3.234))) + expected_circuit = Circuit(register_manager, expected_ir) + + modify_circuit_and_check(circuit, general_merger.merge_single_qubit_gates, expected_circuit) + + assert ir.statements[0].is_anonymous + assert ir.statements[3].generator == Ry + assert ir.statements[3].arguments, (Qubit(0), Float(3.234)) From 78e36926847fa4bdedc76c073f65203a6568c07d Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:00:52 +0200 Subject: [PATCH 04/14] Update tests parser --- test/parser/libqasm/test_libqasm.py | 123 ++++++++++++++-------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/test/parser/libqasm/test_libqasm.py b/test/parser/libqasm/test_libqasm.py index cd991f3e..a5d91ea3 100644 --- a/test/parser/libqasm/test_libqasm.py +++ b/test/parser/libqasm/test_libqasm.py @@ -1,16 +1,18 @@ -import unittest +import pytest -from opensquirrel.default_gates import * +from opensquirrel.default_gates import CNOT, CR, CRk, H, I, Ry, X, default_gate_aliases, default_gate_set +from opensquirrel.ir import Float, Int, Qubit from opensquirrel.parser.libqasm.parser import Parser -class LibqasmTest(unittest.TestCase): - def setUp(self): - self.parser = Parser(gate_set=default_gate_set, gate_aliases=default_gate_aliases) +@pytest.fixture(name="parser") +def parser_fixture() -> Parser: + return Parser(gate_set=default_gate_set, gate_aliases=default_gate_aliases) - def test_simple(self): - circuit = self.parser.circuit_from_string( - """ + +def test_simple(parser: Parser) -> None: + circuit = parser.circuit_from_string( + """ version 3.0 qubit[2] q @@ -22,25 +24,23 @@ def test_simple(self): CR q[1], q[0], 5.123 CRk q[0], q[1], 23 """ - ) - - self.assertEqual(circuit.qubit_register_size, 2) - self.assertEqual(circuit.qubit_register_name, "q") - self.assertEqual( - circuit.ir.statements, - [ - H(Qubit(0)), - I(Qubit(0)), - Ry(Qubit(1), Float(1.234)), - CNOT(Qubit(0), Qubit(1)), - CR(Qubit(1), Qubit(0), Float(5.123)), - CRk(Qubit(0), Qubit(1), Int(23)), - ], - ) - - def test_sgmq(self): - circuit = self.parser.circuit_from_string( - """ + ) + + assert circuit.qubit_register_size == 2 + assert circuit.qubit_register_name == "q" + assert circuit.ir.statements == [ + H(Qubit(0)), + I(Qubit(0)), + Ry(Qubit(1), Float(1.234)), + CNOT(Qubit(0), Qubit(1)), + CR(Qubit(1), Qubit(0), Float(5.123)), + CRk(Qubit(0), Qubit(1), Int(23)), + ] + + +def test_sgmq(parser: Parser) -> None: + circuit = parser.circuit_from_string( + """ version 3.0 qubit[20] q @@ -49,38 +49,37 @@ def test_sgmq(self): X q[13,17] CRk q[0, 3], q[1, 4], 23 """ - ) - - self.assertEqual(circuit.qubit_register_size, 20) - self.assertEqual(circuit.qubit_register_name, "q") - self.assertEqual( - circuit.ir.statements, - [ - H(Qubit(5)), - H(Qubit(6)), - H(Qubit(7)), - H(Qubit(8)), - H(Qubit(9)), - X(Qubit(13)), - X(Qubit(17)), - CRk(Qubit(0), Qubit(1), Int(23)), - CRk(Qubit(3), Qubit(4), Int(23)), - ], - ) - - def test_error(self): - with self.assertRaisesRegex(Exception, "Error at :1:30..31: failed to resolve variable 'q'"): - self.parser.circuit_from_string("""version 3.0; qubit[20] qu; H q[5]""") - - def test_wrong_gate_argument_number_or_types(self): - with self.assertRaisesRegex( - Exception, - r"Parsing error: Error at :1:26\.\.27: failed to resolve instruction 'H' with argument pack \(qubit, int\)", - ): - self.parser.circuit_from_string("""version 3.0; qubit[1] q; H q[0], 1""") - - with self.assertRaisesRegex( - Exception, - r"Parsing error: Error at :1:26\.\.30: failed to resolve instruction 'CNOT' with argument pack \(qubit, int\)", - ): - self.parser.circuit_from_string("""version 3.0; qubit[1] q; CNOT q[0], 1""") + ) + + assert circuit.qubit_register_size == 20 + assert circuit.qubit_register_name == "q" + assert circuit.ir.statements == [ + H(Qubit(5)), + H(Qubit(6)), + H(Qubit(7)), + H(Qubit(8)), + H(Qubit(9)), + X(Qubit(13)), + X(Qubit(17)), + CRk(Qubit(0), Qubit(1), Int(23)), + CRk(Qubit(3), Qubit(4), Int(23)), + ] + + +def test_error(parser: Parser) -> None: + with pytest.raises(Exception, match="Error at :1:30..31: failed to resolve variable 'q'"): + parser.circuit_from_string("version 3.0; qubit[20] qu; H q[5]") + + +def test_wrong_gate_argument_number_or_types(parser: Parser) -> None: + with pytest.raises( + Exception, + match=r"Parsing error: Error at :1:26\.\.27: failed to resolve instruction 'H' with argument pack \(qubit, int\)", + ): + parser.circuit_from_string("version 3.0; qubit[1] q; H q[0], 1") + + with pytest.raises( + Exception, + match=r"Parsing error: Error at :1:26\.\.30: failed to resolve instruction 'CNOT' with argument pack \(qubit, int\)", + ): + parser.circuit_from_string("version 3.0; qubit[1] q; CNOT q[0], 1") From 0f609933fe6b7fffc30774333d9082e1d6348c66 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:01:11 +0200 Subject: [PATCH 05/14] Update tests reindexer --- test/reindexer/test_qubit_reindexer.py | 117 ++++++++++--------------- 1 file changed, 45 insertions(+), 72 deletions(-) diff --git a/test/reindexer/test_qubit_reindexer.py b/test/reindexer/test_qubit_reindexer.py index 63453ee4..88b01280 100644 --- a/test/reindexer/test_qubit_reindexer.py +++ b/test/reindexer/test_qubit_reindexer.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import math -from typing import List import numpy as np import pytest @@ -11,74 +12,46 @@ from opensquirrel.reindexer.qubit_reindexer import get_reindexed_circuit -class TestReindexer: - @pytest.fixture - def replacement_gates_2(self) -> List[Gate]: - return [Y90(Qubit(1)), X(Qubit(3))] - - @pytest.fixture - def replacement_gates_4(self) -> List[Gate]: - return [ - Measure(Qubit(1)), - BlochSphereRotation(Qubit(3), axis=(0, 0, 1), angle=math.pi), - MatrixGate( - np.array( - [ - [1, 0, 0, 0], - [0, 0, 1, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - ] - ), - [Qubit(0), Qubit(3)], - ), - ControlledGate(Qubit(1), X(Qubit(2))), - ] - - @pytest.fixture - def circuit_3_reindexed(self) -> Circuit: - ir = IR() - ir.add_gate(Y90(Qubit(1))) - ir.add_gate(X(Qubit(0))) - return Circuit(RegisterManager(qubit_register_size=2), ir) - - @pytest.fixture - def circuit_4_reindexed(self) -> Circuit: - ir = IR() - ir.add_gate(Measure(Qubit(0))) - ir.add_gate(BlochSphereRotation(Qubit(2), axis=(0, 0, 1), angle=math.pi)) - ir.add_gate( - MatrixGate( - np.array( - [ - [1, 0, 0, 0], - [0, 0, 1, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - ] - ), - [Qubit(1), Qubit(2)], - ) - ) - ir.add_gate(ControlledGate(Qubit(0), X(Qubit(3)))) - return Circuit(RegisterManager(qubit_register_size=4), ir) - - @pytest.fixture - def qubit_indices_2(self) -> List[int]: - return [3, 1] - - @pytest.fixture - def qubit_indices_4(self) -> List[int]: - return [1, 0, 3, 2] - - def test_get_reindexed_circuit_3( - self, replacement_gates_2: List[Gate], qubit_indices_2: List[int], circuit_3_reindexed: Circuit - ) -> None: - circuit_3 = get_reindexed_circuit(replacement_gates_2, qubit_indices_2) - assert circuit_3 == circuit_3_reindexed - - def test_get_reindexed_circuit_4( - self, replacement_gates_4: List[Gate], qubit_indices_4: List[int], circuit_4_reindexed: Circuit - ) -> None: - circuit_4 = get_reindexed_circuit(replacement_gates_4, qubit_indices_4) - assert circuit_4 == circuit_4_reindexed +def circuit_1_reindexed() -> Circuit: + ir = IR() + ir.add_gate(Y90(Qubit(1))) + ir.add_gate(X(Qubit(0))) + return Circuit(RegisterManager(qubit_register_size=2), ir) + + +def replacement_gates_1() -> list[Gate]: + return [Y90(Qubit(1)), X(Qubit(3))] + + +def replacement_gates_2() -> list[Gate]: + return [ + Measure(Qubit(1)), + BlochSphereRotation(Qubit(3), axis=(0, 0, 1), angle=math.pi), + MatrixGate(np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]), [Qubit(0), Qubit(3)]), + ControlledGate(Qubit(1), X(Qubit(2))), + ] + + +def circuit_2_reindexed() -> Circuit: + ir = IR() + ir.add_gate(Measure(Qubit(0))) + ir.add_gate(BlochSphereRotation(Qubit(2), axis=(0, 0, 1), angle=math.pi)) + matrix = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) + ir.add_gate(MatrixGate(matrix, [Qubit(1), Qubit(2)])) + ir.add_gate(ControlledGate(Qubit(0), X(Qubit(3)))) + return Circuit(RegisterManager(qubit_register_size=4), ir) + + +@pytest.mark.parametrize( + "replacement_gates, qubit_indices, circuit_reindexed", + [ + (replacement_gates_1(), [3, 1], circuit_1_reindexed()), + (replacement_gates_2(), [1, 0, 3, 2], circuit_2_reindexed()), + ], + ids=["circuit1", "circuit2"], +) +def test_get_reindexed_circuit( + replacement_gates: list[Gate], qubit_indices: list[int], circuit_reindexed: Circuit +) -> None: + circuit = get_reindexed_circuit(replacement_gates, qubit_indices) + assert circuit == circuit_reindexed From 60bc0f0bc9b829c804bea74068e0755c8e2d6219 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:01:29 +0200 Subject: [PATCH 06/14] Update tests utils --- test/utils/test_matrix_expander.py | 122 +++++++++++++---------------- 1 file changed, 55 insertions(+), 67 deletions(-) diff --git a/test/utils/test_matrix_expander.py b/test/utils/test_matrix_expander.py index 280947a3..fb4ce33d 100644 --- a/test/utils/test_matrix_expander.py +++ b/test/utils/test_matrix_expander.py @@ -1,5 +1,4 @@ import math -import unittest import numpy as np @@ -7,71 +6,60 @@ from opensquirrel.utils import matrix_expander -class MatrixExpanderTest(unittest.TestCase): - def test_bloch_sphere_rotation(self): - gate = BlochSphereRotation(qubit=Qubit(0), axis=(0.8, -0.3, 1.5), angle=0.9468, phase=2.533) - self.assertTrue( - np.allclose( - matrix_expander.get_matrix(gate, 2), - np.array( - [ - [-0.50373461 + 0.83386635j, 0.05578802 + 0.21864595j, 0, 0], - [0.18579927 + 0.12805072j, -0.95671077 + 0.18381011j, 0, 0], - [0, 0, -0.50373461 + 0.83386635j, 0.05578802 + 0.21864595j], - [0, 0, 0.18579927 + 0.12805072j, -0.95671077 + 0.18381011j], - ] - ), - ) - ) +def test_bloch_sphere_rotation() -> None: + gate = BlochSphereRotation(qubit=Qubit(0), axis=(0.8, -0.3, 1.5), angle=0.9468, phase=2.533) + np.testing.assert_almost_equal( + matrix_expander.get_matrix(gate, 2), + [ + [-0.50373461 + 0.83386635j, 0.05578802 + 0.21864595j, 0, 0], + [0.18579927 + 0.12805072j, -0.95671077 + 0.18381011j, 0, 0], + [0, 0, -0.50373461 + 0.83386635j, 0.05578802 + 0.21864595j], + [0, 0, 0.18579927 + 0.12805072j, -0.95671077 + 0.18381011j], + ], + ) - def test_controlled_gate(self): - gate = ControlledGate( - Qubit(2), BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2) - ) - self.assertTrue( - np.allclose( - matrix_expander.get_matrix(gate, 3), - np.array( - [ - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 1, 0], - ] - ), - ) - ) - def test_matrix_gate(self): - gate = MatrixGate( - np.array( - [ - [1, 0, 0, 0], - [0, 0, 1, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - ] - ), - operands=[Qubit(1), Qubit(2)], - ) - self.assertTrue( - np.allclose( - matrix_expander.get_matrix(gate, 3), - np.array( - [ - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 1], - ] - ), - ) - ) +def test_controlled_gate() -> None: + gate = ControlledGate( + Qubit(2), BlochSphereRotation(qubit=Qubit(0), axis=(1, 0, 0), angle=math.pi, phase=math.pi / 2) + ) + np.testing.assert_almost_equal( + matrix_expander.get_matrix(gate, 3), + [ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0], + ], + ) + + +def test_matrix_gate() -> None: + gate = MatrixGate( + np.array( + [ + [1, 0, 0, 0], + [0, 0, 1, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + ] + ), + operands=[Qubit(1), Qubit(2)], + ) + np.testing.assert_almost_equal( + matrix_expander.get_matrix(gate, 3), + [ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1], + ], + ) From d4ebc9e07b073d8fc5cf30cf060f303d1350f2e2 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:01:38 +0200 Subject: [PATCH 07/14] Update tests --- test/ir_equality_test_base.py | 25 ++ test/test_circuit_matrix_calculator.py | 349 ++++++++++++------------- 2 files changed, 186 insertions(+), 188 deletions(-) diff --git a/test/ir_equality_test_base.py b/test/ir_equality_test_base.py index af4ff8da..264c85d3 100644 --- a/test/ir_equality_test_base.py +++ b/test/ir_equality_test_base.py @@ -28,3 +28,28 @@ def modify_circuit_and_check(self, circuit, action, expected_circuit=None): if expected_circuit is not None: self.assertEqual(circuit, expected_circuit) + + +def check_equivalence_up_to_global_phase(matrix_a, matrix_b): + assert are_matrices_equivalent_up_to_global_phase(matrix_a, matrix_b) + + +def modify_circuit_and_check(circuit, action, expected_circuit=None): + """ + Checks whether the action preserves: + - the number of qubits, + - the qubit register name(s), + - the circuit matrix up to a global phase factor. + """ + # Store matrix before decompositions. + expected_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + action(circuit) + + # Get matrix after decompositions. + actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) + + check_equivalence_up_to_global_phase(actual_matrix, expected_matrix) + + if expected_circuit is not None: + assert circuit == expected_circuit diff --git a/test/test_circuit_matrix_calculator.py b/test/test_circuit_matrix_calculator.py index 778524e4..7f4f7058 100644 --- a/test/test_circuit_matrix_calculator.py +++ b/test/test_circuit_matrix_calculator.py @@ -1,195 +1,168 @@ -import unittest +import math + +import numpy as np from opensquirrel import circuit_matrix_calculator from opensquirrel.circuit import Circuit -from opensquirrel.default_gates import * +from opensquirrel.default_gates import CNOT, H, X from opensquirrel.ir import IR, Qubit from opensquirrel.register_manager import RegisterManager -class CircuitMatrixCalculatorTest(unittest.TestCase): - def test_hadamard(self): - register_manager = RegisterManager(qubit_register_size=1) - ir = IR() - ir.add_gate(H(Qubit(0))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [1, 1], - [1, -1], - ] - ), - ) - ) - - def test_double_hadamard(self): - register_manager = RegisterManager(qubit_register_size=1) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(H(Qubit(0))) - circuit = Circuit(register_manager, ir) - - self.assertTrue(np.allclose(circuit_matrix_calculator.get_circuit_matrix(circuit), np.eye(2))) - - def test_triple_hadamard(self): - register_manager = RegisterManager(qubit_register_size=1) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(H(Qubit(0))) - ir.add_gate(H(Qubit(0))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [1, 1], - [1, -1], - ] - ), - ) - ) - - def test_hadamard_x(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(X(Qubit(1))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [0, 0, 1, 1], - [0, 0, 1, -1], - [1, 1, 0, 0], - [1, -1, 0, 0], - ] - ), - ) - ) - - def test_x_hadamard(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(H(Qubit(1))) - ir.add_gate(X(Qubit(0))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [0, 1, 0, 1], - [1, 0, 1, 0], - [0, 1, 0, -1], - [1, 0, -1, 0], - ] - ), - ) - ) - - def test_cnot(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(CNOT(Qubit(1), Qubit(0))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - np.array( - [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - ] - ), - ) - ) - - def test_cnot_reversed(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(CNOT(Qubit(0), Qubit(1))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - np.array( - [ - [1, 0, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - [0, 1, 0, 0], - ] - ), - ) - ) - - def test_hadamard_cnot(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(CNOT(Qubit(0), Qubit(1))) - circuit = Circuit(register_manager, ir) - - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [1, 1, 0, 0], - [0, 0, 1, -1], - [0, 0, 1, 1], - [1, -1, 0, 0], - ] - ), - ) - ) - - def test_hadamard_cnot_0_2(self): - register_manager = RegisterManager(qubit_register_size=3) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_gate(CNOT(Qubit(0), Qubit(2))) - circuit = Circuit(register_manager, ir) - - print(circuit_matrix_calculator.get_circuit_matrix(circuit)) - self.assertTrue( - np.allclose( - circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [1, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, -1, 0, 0], - [0, 0, 1, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, -1], - [0, 0, 0, 0, 1, 1, 0, 0], - [1, -1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 1], - [0, 0, 1, -1, 0, 0, 0, 0], - ] - ), - ) - ) - - -if __name__ == "__main__": - unittest.main() +def test_hadamard() -> None: + register_manager = RegisterManager(qubit_register_size=1) + ir = IR() + ir.add_gate(H(Qubit(0))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), math.sqrt(0.5) * np.array([[1, 1], [1, -1]]) + ) + + +def test_double_hadamard() -> None: + register_manager = RegisterManager(qubit_register_size=1) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(H(Qubit(0))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal(circuit_matrix_calculator.get_circuit_matrix(circuit), np.eye(2)) + + +def test_triple_hadamard() -> None: + register_manager = RegisterManager(qubit_register_size=1) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(H(Qubit(0))) + ir.add_gate(H(Qubit(0))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), math.sqrt(0.5) * np.array([[1, 1], [1, -1]]) + ) + + +def test_hadamard_x() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(X(Qubit(1))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + math.sqrt(0.5) + * np.array( + [ + [0, 0, 1, 1], + [0, 0, 1, -1], + [1, 1, 0, 0], + [1, -1, 0, 0], + ] + ), + ) + + +def test_x_hadamard() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(H(Qubit(1))) + ir.add_gate(X(Qubit(0))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + math.sqrt(0.5) + * np.array( + [ + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 0, -1], + [1, 0, -1, 0], + ] + ), + ) + + +def test_cnot() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(CNOT(Qubit(1), Qubit(0))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + ], + ) + + +def test_cnot_reversed() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(CNOT(Qubit(0), Qubit(1))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + np.array( + [ + [1, 0, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + [0, 1, 0, 0], + ] + ), + ) + + +def test_hadamard_cnot() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(CNOT(Qubit(0), Qubit(1))) + circuit = Circuit(register_manager, ir) + + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + math.sqrt(0.5) + * np.array( + [ + [1, 1, 0, 0], + [0, 0, 1, -1], + [0, 0, 1, 1], + [1, -1, 0, 0], + ] + ), + ) + + +def test_hadamard_cnot_0_2() -> None: + register_manager = RegisterManager(qubit_register_size=3) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_gate(CNOT(Qubit(0), Qubit(2))) + circuit = Circuit(register_manager, ir) + + print(circuit_matrix_calculator.get_circuit_matrix(circuit)) + np.testing.assert_almost_equal( + circuit_matrix_calculator.get_circuit_matrix(circuit), + math.sqrt(0.5) + * np.array( + [ + [1, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, -1, 0, 0], + [0, 0, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, -1], + [0, 0, 0, 0, 1, 1, 0, 0], + [1, -1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 1], + [0, 0, 1, -1, 0, 0, 0, 0], + ] + ), + ) From 0ba419e801b581cb6cd186a05e7375e19ac3a079 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:05:29 +0200 Subject: [PATCH 08/14] Compress matrix calculator tests --- test/test_circuit_matrix_calculator.py | 49 +++----------------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/test/test_circuit_matrix_calculator.py b/test/test_circuit_matrix_calculator.py index 7f4f7058..af374c8d 100644 --- a/test/test_circuit_matrix_calculator.py +++ b/test/test_circuit_matrix_calculator.py @@ -52,15 +52,7 @@ def test_hadamard_x() -> None: np.testing.assert_almost_equal( circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [0, 0, 1, 1], - [0, 0, 1, -1], - [1, 1, 0, 0], - [1, -1, 0, 0], - ] - ), + math.sqrt(0.5) * np.array([[0, 0, 1, 1], [0, 0, 1, -1], [1, 1, 0, 0], [1, -1, 0, 0]]), ) @@ -73,15 +65,7 @@ def test_x_hadamard() -> None: np.testing.assert_almost_equal( circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [0, 1, 0, 1], - [1, 0, 1, 0], - [0, 1, 0, -1], - [1, 0, -1, 0], - ] - ), + math.sqrt(0.5) * np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, -1], [1, 0, -1, 0]]), ) @@ -92,13 +76,7 @@ def test_cnot() -> None: circuit = Circuit(register_manager, ir) np.testing.assert_almost_equal( - circuit_matrix_calculator.get_circuit_matrix(circuit), - [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - ], + circuit_matrix_calculator.get_circuit_matrix(circuit), [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]] ) @@ -109,15 +87,7 @@ def test_cnot_reversed() -> None: circuit = Circuit(register_manager, ir) np.testing.assert_almost_equal( - circuit_matrix_calculator.get_circuit_matrix(circuit), - np.array( - [ - [1, 0, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - [0, 1, 0, 0], - ] - ), + circuit_matrix_calculator.get_circuit_matrix(circuit), [[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]] ) @@ -130,15 +100,7 @@ def test_hadamard_cnot() -> None: np.testing.assert_almost_equal( circuit_matrix_calculator.get_circuit_matrix(circuit), - math.sqrt(0.5) - * np.array( - [ - [1, 1, 0, 0], - [0, 0, 1, -1], - [0, 0, 1, 1], - [1, -1, 0, 0], - ] - ), + math.sqrt(0.5) * np.array([[1, 1, 0, 0], [0, 0, 1, -1], [0, 0, 1, 1], [1, -1, 0, 0]]), ) @@ -149,7 +111,6 @@ def test_hadamard_cnot_0_2() -> None: ir.add_gate(CNOT(Qubit(0), Qubit(2))) circuit = Circuit(register_manager, ir) - print(circuit_matrix_calculator.get_circuit_matrix(circuit)) np.testing.assert_almost_equal( circuit_matrix_calculator.get_circuit_matrix(circuit), math.sqrt(0.5) From 8f52f9467375cd42884816d6c77c1d5c17de2bb0 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:19:02 +0200 Subject: [PATCH 09/14] Update integration tests --- test/test_integration.py | 604 ++++++++++++++++++++------------------- 1 file changed, 303 insertions(+), 301 deletions(-) diff --git a/test/test_integration.py b/test/test_integration.py index fdcbbe8c..ae5974c9 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -1,56 +1,56 @@ # This integration test also serves as example and code documentation. import importlib.util -import unittest + +import pytest from opensquirrel.circuit import Circuit from opensquirrel.decomposer.cnot_decomposer import CNOTDecomposer from opensquirrel.decomposer.mckay_decomposer import McKayDecomposer from opensquirrel.decomposer.zyz_decomposer import ZYZDecomposer -from opensquirrel.default_gates import * +from opensquirrel.default_gates import CNOT, CZ, H from opensquirrel.exporter.export_format import ExportFormat -class IntegrationTest(unittest.TestCase): - def test_simple(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit[3] q - - Ry(1.23) q[0] - Ry(2.34) q[1] - CNOT q[0], q[1] - Rx(-2.3) q[0] - Ry(-3.14) q[1] - """ - ) - - # Decompose CNOT as - # - # -----•----- ------- Z ------- - # | == | - # -----⊕---- --- H --•-- H --- - # - - circuit.replace( - CNOT, - lambda control, target: [ - H(target), - CZ(control, target), - H(target), - ], - ) - - # Do 1q-gate fusion and decomposer with McKay decomposition. - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - - # Write the transformed circuit as a cQASM v3 string. - self.assertEqual( - str(circuit), - """version 3.0 +def test_simple() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit[3] q + + Ry(1.23) q[0] + Ry(2.34) q[1] + CNOT q[0], q[1] + Rx(-2.3) q[0] + Ry(-3.14) q[1] + """ + ) + + # Decompose CNOT as + # + # -----•----- ------- Z ------- + # | == | + # -----⊕---- --- H --•-- H --- + # + + circuit.replace( + CNOT, + lambda control, target: [ + H(target), + CZ(control, target), + H(target), + ], + ) + + # Do 1q-gate fusion and decomposer with McKay decomposition. + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + + # Write the transformed circuit as a cQASM v3 string. + assert ( + str(circuit) + == """version 3.0 qubit[3] q @@ -74,32 +74,33 @@ def test_simple(self): Rz(1.5723889) q[1] X90 q[1] Rz(3.1415927) q[1] -""", - ) - - def test_measurement(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit[3] q - bit[3] b - - Ry(2.34) q[2] - Rz(1.5707963) q[0] - Ry(-0.2) q[0] - CNOT q[1], q[0] - Rz(1.5789) q[0] - CNOT q[1], q[0] - Rz(2.5707963) q[1] - b[0, 2] = measure q[0, 2] - """, - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - self.assertEqual( - str(circuit), - """version 3.0 +""" + ) + + +def test_measurement() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit[3] q + bit[3] b + + Ry(2.34) q[2] + Rz(1.5707963) q[0] + Ry(-0.2) q[0] + CNOT q[1], q[0] + Rz(1.5789) q[0] + CNOT q[1], q[0] + Rz(2.5707963) q[1] + b[0, 2] = measure q[0, 2] + """, + ) + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + assert ( + str(circuit) + == """version 3.0 qubit[3] q @@ -118,30 +119,31 @@ def test_measurement(self): X90 q[2] measure q[2] Rz(2.5707963) q[1] -""", - ) - - def test_consecutive_measurements(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit[3] q - bit[3] b - - H q[0] - H q[1] - H q[2] - b[0] = measure q[0] - b[1] = measure q[1] - b[2] = measure q[2] - """ - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - self.assertEqual( - str(circuit), - """version 3.0 +""" + ) + + +def test_consecutive_measurements() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit[3] q + bit[3] b + + H q[0] + H q[1] + H q[2] + b[0] = measure q[0] + b[1] = measure q[1] + b[2] = measure q[2] + """ + ) + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + assert ( + str(circuit) + == """version 3.0 qubit[3] q @@ -157,28 +159,29 @@ def test_consecutive_measurements(self): Rz(1.5707963) q[2] X90 q[2] measure q[2] -""", - ) - - def test_measurements_unrolling(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit[3] q - bit[3] b - - H q[0] - H q[1] - H q[2] - b = measure q - """ - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - self.assertEqual( - str(circuit), - """version 3.0 +""" + ) + + +def test_measurements_unrolling() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit[3] q + bit[3] b + + H q[0] + H q[1] + H q[2] + b = measure q + """ + ) + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + assert ( + str(circuit) + == """version 3.0 qubit[3] q @@ -194,27 +197,28 @@ def test_measurements_unrolling(self): Rz(1.5707963) q[2] X90 q[2] measure q[2] -""", - ) +""" + ) - def test_measure_order(self): - circuit = Circuit.from_string( - """ - version 3.0 - qubit[2] q - bit[2] b +def test_measure_order() -> None: + circuit = Circuit.from_string( + """ + version 3.0 - Rz(-2.3561945) q[1] - Rz(1.5707963) q[1] - b[1, 0] = measure q[1, 0] - """ - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - self.assertEqual( - str(circuit), - """version 3.0 + qubit[2] q + bit[2] b + + Rz(-2.3561945) q[1] + Rz(1.5707963) q[1] + b[1, 0] = measure q[1, 0] + """ + ) + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + assert ( + str(circuit) + == """version 3.0 qubit[2] q @@ -225,32 +229,33 @@ def test_measure_order(self): Rz(-0.3926991) q[1] measure q[1] measure q[0] -""", - ) - - def test_multiple_qubit_bit_definitions_and_mid_circuit_measure_instructions(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit q0 - bit b0 - X q0 - b0 = measure q0 - - qubit q1 - bit b1 - H q1 - CNOT q1, q0 - b1 = measure q1 - b0 = measure q0 - """ - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) - self.assertEqual( - str(circuit), - """version 3.0 +""" + ) + + +def test_multiple_qubit_bit_definitions_and_mid_circuit_measure_instructions() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit q0 + bit b0 + X q0 + b0 = measure q0 + + qubit q1 + bit b1 + H q1 + CNOT q1, q0 + b1 = measure q1 + b0 = measure q0 + """ + ) + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + assert ( + str(circuit) + == """version 3.0 qubit[2] q @@ -265,45 +270,46 @@ def test_multiple_qubit_bit_definitions_and_mid_circuit_measure_instructions(sel CNOT q[1], q[0] measure q[1] measure q[0] -""", - ) +""" + ) - def test_qi(self): - circuit = Circuit.from_string( - """ - version 3.0 - - // This is a single line comment which ends on the newline. - // The cQASM string must begin with the version instruction (apart from any preceding comments). - - /* This is a multi- - line comment block */ - - qubit[4] q - - // Let us create a Bell state on 2 qubits and a |+> state on the third qubit - - H q[2] - H q[1] - H q[0] - Rz(1.5707963) q[0] - Ry(-0.2) q[0] - CNOT q[1], q[0] - Rz(1.5789) q[0] - CNOT q[1], q[0] - CNOT q[1], q[2] - Rz(2.5707963) q[1] - CR(2.123) q[2], q[3] - Ry(-1.5707963) q[1] - """ - ) - circuit.merge_single_qubit_gates() - circuit.decompose(decomposer=McKayDecomposer()) +def test_qi() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + // This is a single line comment which ends on the newline. + // The cQASM string must begin with the version instruction (apart from any preceding comments). - self.assertEqual( - str(circuit), - """version 3.0 + /* This is a multi- + line comment block */ + + qubit[4] q + + // Let us create a Bell state on 2 qubits and a |+> state on the third qubit + + H q[2] + H q[1] + H q[0] + Rz(1.5707963) q[0] + Ry(-0.2) q[0] + CNOT q[1], q[0] + Rz(1.5789) q[0] + CNOT q[1], q[0] + CNOT q[1], q[2] + Rz(2.5707963) q[1] + CR(2.123) q[2], q[3] + Ry(-1.5707963) q[1] + """ + ) + + circuit.merge_single_qubit_gates() + circuit.decompose(decomposer=McKayDecomposer()) + + assert ( + str(circuit) + == """version 3.0 qubit[4] q @@ -328,122 +334,118 @@ def test_qi(self): Rz(1.5707963) q[1] X90 q[1] Rz(3.1415927) q[1] -""", - ) +""" + ) - def test_libqasm_error(self): - with self.assertRaisesRegex( - Exception, - r"Parsing error: Error at :4:17\.\.19: failed to resolve instruction 'Ry' with argument pack \(qubit, float, int\)", - ): - Circuit.from_string( - """ + +def test_libqasm_error() -> None: + with pytest.raises( + Exception, + match=r"Parsing error: Error at :4:17\.\.19: failed to resolve instruction 'Ry' with argument pack \(qubit, float, int\)", + ): + Circuit.from_string( + """ version 3.0 qubit[3] q Ry q[0], 1.23, 1 """, - ) - - def test_export_quantify_scheduler(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit[3] q - bit[3] b - - H q[1] - CZ q[0], q[1] - CNOT q[0], q[1] - CRk(4) q[0], q[1] - H q[0] - b[0:1] = measure q[0:1] - """ ) - circuit.decompose(decomposer=CNOTDecomposer()) - - # Quantify-scheduler prefers CZ. - circuit.replace( - CNOT, - lambda control, target: [ - H(target), - CZ(control, target), - H(target), - ], - ) - # Reduce gate count by single-qubit gate fusion. - circuit.merge_single_qubit_gates() - - # FIXME: for best gate count we need a Z-XY decomposer. - # See https://github.com/QuTech-Delft/OpenSquirrel/issues/98 - circuit.decompose(decomposer=ZYZDecomposer()) - - if importlib.util.find_spec("quantify_scheduler") is None: - with self.assertRaisesRegex( - Exception, "quantify-scheduler is not installed, or cannot be installed on " "your system" - ): - circuit.export(fmt=ExportFormat.QUANTIFY_SCHEDULER) - else: - exported_schedule = circuit.export(fmt=ExportFormat.QUANTIFY_SCHEDULER) - - self.assertEqual(exported_schedule.name, "Exported OpenSquirrel circuit") - - operations = [ - exported_schedule.operations[schedulable["operation_id"]].name - for schedulable in exported_schedule.schedulables.values() - ] - - self.assertEqual( - operations, - [ - "Rz(-180, 'q[1]')", - "Rxy(90, 90, 'q[1]')", - "CZ (q[0], q[1])", - "Rz(-180, 'q[1]')", - "Rxy(90, 90, 'q[1]')", - "CZ (q[0], q[1])", - "Rz(90, 'q[1]')", - "Rxy(11.25, 90, 'q[1]')", - "Rz(-90, 'q[1]')", - "CZ (q[0], q[1])", - "Rz(90, 'q[1]')", - "Rxy(-11.25, 90, 'q[1]')", - "Rz(-90, 'q[1]')", - "CZ (q[0], q[1])", - "Rz(11.25, 'q[0]')", - "Rxy(-90, 90, 'q[0]')", - "Rz(-180, 'q[0]')", - "Rz(-180, 'q[1]')", - "Rxy(90, 90, 'q[1]')", - "Measure q[0]", - "Measure q[1]", - ], - ) - - def test_merge_y90_x_to_h(self): - circuit = Circuit.from_string( - """ - version 3.0 - - qubit q - - Y90 q - X q - """ - ) - circuit.merge_single_qubit_gates() - self.assertEqual( - str(circuit), - """version 3.0 +def test_export_quantify_scheduler() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit[3] q + bit[3] b + + H q[1] + CZ q[0], q[1] + CNOT q[0], q[1] + CRk(4) q[0], q[1] + H q[0] + b[0:1] = measure q[0:1] + """ + ) + + circuit.decompose(decomposer=CNOTDecomposer()) + + # Quantify-scheduler prefers CZ. + circuit.replace( + CNOT, + lambda control, target: [ + H(target), + CZ(control, target), + H(target), + ], + ) + + # Reduce gate count by single-qubit gate fusion. + circuit.merge_single_qubit_gates() + + # FIXME: for best gate count we need a Z-XY decomposer. + # See https://github.com/QuTech-Delft/OpenSquirrel/issues/98 + circuit.decompose(decomposer=ZYZDecomposer()) + + if importlib.util.find_spec("quantify_scheduler") is None: + with pytest.raises( + Exception, match="quantify-scheduler is not installed, or cannot be installed on " "your system" + ): + circuit.export(fmt=ExportFormat.QUANTIFY_SCHEDULER) + else: + exported_schedule = circuit.export(fmt=ExportFormat.QUANTIFY_SCHEDULER) + + assert exported_schedule.name == "Exported OpenSquirrel circuit" + + operations = [ + exported_schedule.operations[schedulable["operation_id"]].name + for schedulable in exported_schedule.schedulables.values() + ] + + assert operations == [ + "Rz(-180, 'q[1]')", + "Rxy(90, 90, 'q[1]')", + "CZ (q[0], q[1])", + "Rz(-180, 'q[1]')", + "Rxy(90, 90, 'q[1]')", + "CZ (q[0], q[1])", + "Rz(90, 'q[1]')", + "Rxy(11.25, 90, 'q[1]')", + "Rz(-90, 'q[1]')", + "CZ (q[0], q[1])", + "Rz(90, 'q[1]')", + "Rxy(-11.25, 90, 'q[1]')", + "Rz(-90, 'q[1]')", + "CZ (q[0], q[1])", + "Rz(11.25, 'q[0]')", + "Rxy(-90, 90, 'q[0]')", + "Rz(-180, 'q[0]')", + "Rz(-180, 'q[1]')", + "Rxy(90, 90, 'q[1]')", + "Measure q[0]", + "Measure q[1]", + ] + + +def test_merge_y90_x_to_h() -> None: + circuit = Circuit.from_string( + """ + version 3.0 + + qubit q + + Y90 q + X q + """ + ) + circuit.merge_single_qubit_gates() + assert ( + str(circuit) + == """version 3.0 qubit[1] q H q[0] -""", - ) - - -if __name__ == "__main__": - unittest.main() +""" + ) From 0af6a7fdd9fce970c4dbe622352b8f11dca41a3f Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 15:51:55 +0200 Subject: [PATCH 10/14] Update tests replacer --- test/test_ir.py | 1 - test/test_replacer.py | 108 +++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/test/test_ir.py b/test/test_ir.py index b711f883..16111931 100644 --- a/test/test_ir.py +++ b/test/test_ir.py @@ -4,7 +4,6 @@ import numpy as np import pytest -from numpy.typing import NDArray from opensquirrel.common import ATOL from opensquirrel.ir import BlochSphereRotation, ControlledGate, MatrixGate, Measure, Qubit diff --git a/test/test_replacer.py b/test/test_replacer.py index a213117c..3db7a68f 100644 --- a/test/test_replacer.py +++ b/test/test_replacer.py @@ -1,52 +1,58 @@ from __future__ import annotations -import unittest +import math + +import pytest from opensquirrel.circuit import Circuit from opensquirrel.decomposer import general_decomposer from opensquirrel.decomposer.general_decomposer import Decomposer, check_gate_replacement -from opensquirrel.default_gates import * -from opensquirrel.ir import IR, Comment, Qubit +from opensquirrel.default_gates import CNOT, Y90, BlochSphereRotation, H, I, Ry, Rz, X, Z, sqrtSWAP +from opensquirrel.ir import IR, Comment, Float, Gate, Qubit from opensquirrel.register_manager import RegisterManager -class ReplacerTest(unittest.TestCase): - @staticmethod - def test_check_valid_replacement(): - check_gate_replacement(BlochSphereRotation.identity(Qubit(0)), [BlochSphereRotation.identity(Qubit(0))]) - - check_gate_replacement( - BlochSphereRotation.identity(Qubit(0)), - [BlochSphereRotation.identity(Qubit(0)), BlochSphereRotation.identity(Qubit(0))], - ) - - check_gate_replacement(BlochSphereRotation.identity(Qubit(0)), [H(Qubit(0)), H(Qubit(0))]) - - check_gate_replacement(H(Qubit(0)), [H(Qubit(0)), H(Qubit(0)), H(Qubit(0))]) - - check_gate_replacement( - CNOT(Qubit(0), Qubit(1)), [CNOT(Qubit(0), Qubit(1)), BlochSphereRotation.identity(Qubit(0))] - ) - - # Arbitrary global phase change is not considered an issue. - check_gate_replacement( - CNOT(Qubit(0), Qubit(1)), - [CNOT(Qubit(0), Qubit(1)), BlochSphereRotation(Qubit(0), angle=0, axis=(1, 0, 0), phase=621.6546)], - ) - - def test_check_valid_replacement_wrong_qubit(self): - with self.assertRaisesRegex(Exception, r"Replacement for gate H does not seem to operate on the right qubits"): - check_gate_replacement(H(Qubit(0)), [H(Qubit(1))]) - - with self.assertRaisesRegex( - Exception, r"Replacement for gate CNOT does not seem to operate on the right qubits" - ): - check_gate_replacement(CNOT(Qubit(0), Qubit(1)), [CNOT(Qubit(2), Qubit(1))]) - - with self.assertRaisesRegex(Exception, r"Replacement for gate CNOT does not preserve the quantum state"): - check_gate_replacement(CNOT(Qubit(0), Qubit(1)), [CNOT(Qubit(1), Qubit(0))]) - - def test_check_valid_replacement_cnot_as_sqrt_swap(self): +class TestCheckGateReplacement: + + @pytest.mark.parametrize( + "gate, replacement_gates", + [ + (I(Qubit(0)), [I(Qubit(0))]), + (I(Qubit(0)), [I(Qubit(0)), I(Qubit(0))]), + (I(Qubit(0)), [H(Qubit(0)), H(Qubit(0))]), + (H(Qubit(0)), [H(Qubit(0)), H(Qubit(0)), H(Qubit(0))]), + (CNOT(Qubit(0), Qubit(1)), [CNOT(Qubit(0), Qubit(1)), I(Qubit(0))]), + # Arbitrary global phase change is not considered an issue. + ( + CNOT(Qubit(0), Qubit(1)), + [CNOT(Qubit(0), Qubit(1)), BlochSphereRotation(Qubit(0), angle=0, axis=(1, 0, 0), phase=621.6546)], + ), + ], + ) + def test_valid_replacement(self, gate: Gate, replacement_gates: list[Gate]) -> None: + check_gate_replacement(gate, replacement_gates) + + @pytest.mark.parametrize( + "gate, replacement_gates, error_msg", + [ + (H(Qubit(0)), [H(Qubit(1))], "Replacement for gate H does not seem to operate on the right qubits"), + ( + CNOT(Qubit(0), Qubit(1)), + [CNOT(Qubit(2), Qubit(1))], + "Replacement for gate CNOT does not seem to operate on the right qubits", + ), + ( + CNOT(Qubit(0), Qubit(1)), + [CNOT(Qubit(1), Qubit(0))], + "Replacement for gate CNOT does not preserve the quantum state", + ), + ], + ) + def test_wrong_qubit(self, gate: Gate, replacement_gates: list[Gate], error_msg: str) -> None: + with pytest.raises(Exception, match=error_msg): + check_gate_replacement(gate, replacement_gates) + + def test_cnot_as_sqrt_swap(self): # https://en.wikipedia.org/wiki/Quantum_logic_gate#/media/File:Qcircuit_CNOTsqrtSWAP2.svg c = Qubit(0) t = Qubit(1) @@ -63,7 +69,7 @@ def test_check_valid_replacement_cnot_as_sqrt_swap(self): ], ) - with self.assertRaisesRegex(Exception, r"Replacement for gate CNOT does not preserve the quantum state"): + with pytest.raises(Exception, match="Replacement for gate CNOT does not preserve the quantum state"): check_gate_replacement( CNOT(control=c, target=t), [ @@ -77,9 +83,7 @@ def test_check_valid_replacement_cnot_as_sqrt_swap(self): ], ) - with self.assertRaisesRegex( - Exception, r"Replacement for gate CNOT does not seem to operate on the right qubits" - ): + with pytest.raises(Exception, match="Replacement for gate CNOT does not seem to operate on the right qubits"): check_gate_replacement( CNOT(control=c, target=t), [ @@ -93,16 +97,18 @@ def test_check_valid_replacement_cnot_as_sqrt_swap(self): ], ) - def test_check_valid_replacement_large_number_of_qubits(self): + def test_large_number_of_qubits(self): # If we were building the whole circuit matrix, this would run out of memory. check_gate_replacement(H(Qubit(9234687)), [Y90(Qubit(9234687)), X(Qubit(9234687))]) - with self.assertRaisesRegex(Exception, r"Replacement for gate H does not seem to operate on the right qubits"): + with pytest.raises(Exception, match="Replacement for gate H does not seem to operate on the right qubits"): check_gate_replacement(H(Qubit(9234687)), [Y90(Qubit(698446519)), X(Qubit(9234687))]) - with self.assertRaisesRegex(Exception, r"Replacement for gate H does not preserve the quantum state"): + with pytest.raises(Exception, match="Replacement for gate H does not preserve the quantum state"): check_gate_replacement(H(Qubit(9234687)), [Y90(Qubit(9234687)), X(Qubit(9234687)), X(Qubit(9234687))]) + +class TestReplacer: def test_replace_generic(self): register_manager = RegisterManager(qubit_register_size=3) ir = IR() @@ -126,7 +132,7 @@ def decompose(self, g: Gate) -> list[Gate]: expected_ir.add_gate(CNOT(Qubit(0), Qubit(1))) expected_circuit = Circuit(register_manager, expected_ir) - self.assertEqual(expected_circuit, circuit) + assert expected_circuit == circuit def test_replace(self): register_manager = RegisterManager(qubit_register_size=3) @@ -143,8 +149,4 @@ def test_replace(self): expected_ir.add_comment(Comment("Test comment.")) expected_circuit = Circuit(register_manager, expected_ir) - self.assertEqual(expected_circuit, circuit) - - -if __name__ == "__main__": - unittest.main() + assert expected_circuit == circuit From f5a8e5419bf0a249ea15c1396a03b04f34a90bce Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 16:00:44 +0200 Subject: [PATCH 11/14] Update tests zyz decomposer --- test/test_writer.py | 116 ++++++++++++++-------------- test/test_zyz_decomposer.py | 146 ++++++++++++++---------------------- 2 files changed, 112 insertions(+), 150 deletions(-) diff --git a/test/test_writer.py b/test/test_writer.py index 736e46c9..d19740a2 100644 --- a/test/test_writer.py +++ b/test/test_writer.py @@ -1,73 +1,72 @@ -import unittest - from opensquirrel.circuit import Circuit -from opensquirrel.default_gates import * +from opensquirrel.default_gates import CR, H from opensquirrel.exporter import writer -from opensquirrel.ir import IR, Comment, Float, Qubit +from opensquirrel.ir import IR, BlochSphereRotation, Comment, Float, Qubit from opensquirrel.register_manager import RegisterManager -class WriterTest(unittest.TestCase): - def test_write(self): - register_manager = RegisterManager(qubit_register_size=3) - ir = IR() - circuit = Circuit(register_manager, ir) +def test_write() -> None: + register_manager = RegisterManager(qubit_register_size=3) + ir = IR() + circuit = Circuit(register_manager, ir) - self.assertEqual( - writer.circuit_to_string(circuit), - """version 3.0 + assert ( + writer.circuit_to_string(circuit) + == """version 3.0 qubit[3] q -""", - ) +""" + ) - ir.add_gate(H(Qubit(0))) - ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) - circuit = Circuit(register_manager, ir) + ir.add_gate(H(Qubit(0))) + ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) + circuit = Circuit(register_manager, ir) - self.assertEqual( - writer.circuit_to_string(circuit), - """version 3.0 + assert ( + writer.circuit_to_string(circuit) + == """version 3.0 qubit[3] q H q[0] CR(1.234) q[0], q[1] -""", - ) +""" + ) + - def test_anonymous_gate(self): - register_manager = RegisterManager(qubit_register_size=2) - ir = IR() - ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) - ir.add_gate(BlochSphereRotation(Qubit(0), axis=(1, 1, 1), angle=1.23)) - ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) - circuit = Circuit(register_manager, ir) +def test_anonymous_gate() -> None: + register_manager = RegisterManager(qubit_register_size=2) + ir = IR() + ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) + ir.add_gate(BlochSphereRotation(Qubit(0), axis=(1, 1, 1), angle=1.23)) + ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) + circuit = Circuit(register_manager, ir) - self.assertEqual( - writer.circuit_to_string(circuit), - """version 3.0 + assert ( + writer.circuit_to_string(circuit) + == """version 3.0 qubit[2] q CR(1.234) q[0], q[1] CR(1.234) q[0], q[1] -""", - ) +""" + ) + - def test_comment(self): - register_manager = RegisterManager(qubit_register_size=3) - ir = IR() - ir.add_gate(H(Qubit(0))) - ir.add_comment(Comment("My comment")) - ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) - circuit = Circuit(register_manager, ir) +def test_comment() -> None: + register_manager = RegisterManager(qubit_register_size=3) + ir = IR() + ir.add_gate(H(Qubit(0))) + ir.add_comment(Comment("My comment")) + ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.234))) + circuit = Circuit(register_manager, ir) - self.assertEqual( - writer.circuit_to_string(circuit), - """version 3.0 + assert ( + writer.circuit_to_string(circuit) + == """version 3.0 qubit[3] q @@ -76,25 +75,22 @@ def test_comment(self): /* My comment */ CR(1.234) q[0], q[1] -""", - ) +""" + ) - def test_cap_significant_digits(self): - register_manager = RegisterManager(qubit_register_size=3) - ir = IR() - ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.6546514861321684321654))) - circuit = Circuit(register_manager, ir) - self.assertEqual( - writer.circuit_to_string(circuit), - """version 3.0 +def test_cap_significant_digits() -> None: + register_manager = RegisterManager(qubit_register_size=3) + ir = IR() + ir.add_gate(CR(Qubit(0), Qubit(1), Float(1.6546514861321684321654))) + circuit = Circuit(register_manager, ir) + + assert ( + writer.circuit_to_string(circuit) + == """version 3.0 qubit[3] q CR(1.6546515) q[0], q[1] -""", - ) - - -if __name__ == "__main__": - unittest.main() +""" + ) diff --git a/test/test_zyz_decomposer.py b/test/test_zyz_decomposer.py index a3747ed2..95d7a1da 100644 --- a/test/test_zyz_decomposer.py +++ b/test/test_zyz_decomposer.py @@ -1,92 +1,58 @@ -import unittest -from test.ir_equality_test_base import IREqualityTestBase +import math from opensquirrel.decomposer.zyz_decomposer import ZYZDecomposer -from opensquirrel.default_gates import * -from opensquirrel.ir import Float, Qubit - - -class ZYZDecomposerTest(IREqualityTestBase): - def test_ignores_2q_gates(self): - self.assertEqual(ZYZDecomposer().decompose(CNOT(Qubit(0), Qubit(1))), [CNOT(Qubit(0), Qubit(1))]) - self.assertEqual( - ZYZDecomposer().decompose(CR(Qubit(2), Qubit(3), Float(2.123))), [CR(Qubit(2), Qubit(3), Float(2.123))] - ) - - def test_identity_empty_decomposition(self): - self.assertEqual(ZYZDecomposer().decompose(BlochSphereRotation.identity(Qubit(0))), []) - - def test_x(self): - self.assertEqual( - ZYZDecomposer().decompose(X(Qubit(0))), - [ - S(Qubit(0)), - Ry(Qubit(0), Float(math.pi)), - Sdag(Qubit(0)), - ], - ) - - def test_x_arbitrary(self): - self.assertEqual( - ZYZDecomposer().decompose(Rx(Qubit(0), Float(0.9))), - [ - S(Qubit(0)), - Ry(Qubit(0), Float(0.9)), - Sdag(Qubit(0)), - ], - ) - - def test_y(self): - self.assertEqual( - ZYZDecomposer().decompose(Y(Qubit(0))), - [ - Ry(Qubit(0), Float(math.pi)), - ], - ) - - def test_y_arbitrary(self): - self.assertEqual( - ZYZDecomposer().decompose(Ry(Qubit(0), Float(0.9))), - [ - Ry(Qubit(0), Float(0.9)), - ], - ) - - def test_z(self): - self.assertEqual( - ZYZDecomposer().decompose(Z(Qubit(0))), - [ - Rz(Qubit(0), Float(math.pi)), - ], - ) - - def test_z_arbitrary(self): - self.assertEqual( - ZYZDecomposer().decompose(Rz(Qubit(0), Float(0.123))), - [ - Rz(Qubit(0), Float(0.123)), - ], - ) - - def test_hadamard(self): - self.assertEqual( - ZYZDecomposer().decompose(H(Qubit(0))), - [ - Rz(Qubit(0), Float(math.pi)), - Ry(Qubit(0), Float(math.pi / 2)), - ], - ) - - def test_arbitrary(self): - self.assertEqual( - ZYZDecomposer().decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)), - [ - Rz(Qubit(0), Float(0.018644578210710527)), - Ry(Qubit(0), Float(-0.6209410696845807)), - Rz(Qubit(0), Float(-0.9086506397909061)), - ], - ) - - -if __name__ == "__main__": - unittest.main() +from opensquirrel.default_gates import CNOT, CR, H, Rx, Ry, Rz, S, Sdag, X, Y, Z +from opensquirrel.ir import BlochSphereRotation, Float, Qubit + + +def test_ignores_2q_gates() -> None: + ZYZDecomposer().decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))] + assert ZYZDecomposer().decompose(CR(Qubit(2), Qubit(3), Float(2.123))) == [CR(Qubit(2), Qubit(3), Float(2.123))] + + +def test_identity_empty_decomposition() -> None: + assert ZYZDecomposer().decompose(BlochSphereRotation.identity(Qubit(0))) == [] + + +def test_x() -> None: + assert ZYZDecomposer().decompose(X(Qubit(0))) == [ + S(Qubit(0)), + Ry(Qubit(0), Float(math.pi)), + Sdag(Qubit(0)), + ] + + +def test_x_arbitrary() -> None: + assert ZYZDecomposer().decompose(Rx(Qubit(0), Float(0.9))) == [ + S(Qubit(0)), + Ry(Qubit(0), Float(0.9)), + Sdag(Qubit(0)), + ] + + +def test_y() -> None: + assert ZYZDecomposer().decompose(Y(Qubit(0))) == [Ry(Qubit(0), Float(math.pi))] + + +def test_y_arbitrary() -> None: + assert ZYZDecomposer().decompose(Ry(Qubit(0), Float(0.9))) == [Ry(Qubit(0), Float(0.9))] + + +def test_z() -> None: + assert ZYZDecomposer().decompose(Z(Qubit(0))) == [Rz(Qubit(0), Float(math.pi))] + + +def test_z_arbitrary() -> None: + assert ZYZDecomposer().decompose(Rz(Qubit(0), Float(0.123))) == [Rz(Qubit(0), Float(0.123))] + + +def test_hadamard() -> None: + assert ZYZDecomposer().decompose(H(Qubit(0))) == [Rz(Qubit(0), Float(math.pi)), Ry(Qubit(0), Float(math.pi / 2))] + + +def test_arbitrary() -> None: + assert ZYZDecomposer().decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)) == [ + Rz(Qubit(0), Float(0.018644578210710527)), + Ry(Qubit(0), Float(-0.6209410696845807)), + Rz(Qubit(0), Float(-0.9086506397909061)), + ] From 94e91567fcf5bdebfdb18f6c331b6353bea34555 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 16:53:57 +0200 Subject: [PATCH 12/14] Update tests zyz decomposer --- test/test_zyz_decomposer.py | 92 +++++++++++++++---------------------- 1 file changed, 37 insertions(+), 55 deletions(-) diff --git a/test/test_zyz_decomposer.py b/test/test_zyz_decomposer.py index 95d7a1da..d9846de6 100644 --- a/test/test_zyz_decomposer.py +++ b/test/test_zyz_decomposer.py @@ -1,58 +1,40 @@ import math -from opensquirrel.decomposer.zyz_decomposer import ZYZDecomposer -from opensquirrel.default_gates import CNOT, CR, H, Rx, Ry, Rz, S, Sdag, X, Y, Z -from opensquirrel.ir import BlochSphereRotation, Float, Qubit - - -def test_ignores_2q_gates() -> None: - ZYZDecomposer().decompose(CNOT(Qubit(0), Qubit(1))) == [CNOT(Qubit(0), Qubit(1))] - assert ZYZDecomposer().decompose(CR(Qubit(2), Qubit(3), Float(2.123))) == [CR(Qubit(2), Qubit(3), Float(2.123))] - - -def test_identity_empty_decomposition() -> None: - assert ZYZDecomposer().decompose(BlochSphereRotation.identity(Qubit(0))) == [] - - -def test_x() -> None: - assert ZYZDecomposer().decompose(X(Qubit(0))) == [ - S(Qubit(0)), - Ry(Qubit(0), Float(math.pi)), - Sdag(Qubit(0)), - ] - - -def test_x_arbitrary() -> None: - assert ZYZDecomposer().decompose(Rx(Qubit(0), Float(0.9))) == [ - S(Qubit(0)), - Ry(Qubit(0), Float(0.9)), - Sdag(Qubit(0)), - ] - - -def test_y() -> None: - assert ZYZDecomposer().decompose(Y(Qubit(0))) == [Ry(Qubit(0), Float(math.pi))] +import pytest - -def test_y_arbitrary() -> None: - assert ZYZDecomposer().decompose(Ry(Qubit(0), Float(0.9))) == [Ry(Qubit(0), Float(0.9))] - - -def test_z() -> None: - assert ZYZDecomposer().decompose(Z(Qubit(0))) == [Rz(Qubit(0), Float(math.pi))] - - -def test_z_arbitrary() -> None: - assert ZYZDecomposer().decompose(Rz(Qubit(0), Float(0.123))) == [Rz(Qubit(0), Float(0.123))] - - -def test_hadamard() -> None: - assert ZYZDecomposer().decompose(H(Qubit(0))) == [Rz(Qubit(0), Float(math.pi)), Ry(Qubit(0), Float(math.pi / 2))] - - -def test_arbitrary() -> None: - assert ZYZDecomposer().decompose(BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324)) == [ - Rz(Qubit(0), Float(0.018644578210710527)), - Ry(Qubit(0), Float(-0.6209410696845807)), - Rz(Qubit(0), Float(-0.9086506397909061)), - ] +from opensquirrel.decomposer.zyz_decomposer import ZYZDecomposer +from opensquirrel.default_gates import CNOT, CR, H, I, Rx, Ry, Rz, S, Sdag, X, Y, Z +from opensquirrel.ir import BlochSphereRotation, Float, Gate, Qubit + + +@pytest.fixture(name="decomposer") +def decomposer_fixture() -> ZYZDecomposer: + return ZYZDecomposer() + + +@pytest.mark.parametrize( + "gate, expected_result", + [ + (CNOT(Qubit(0), Qubit(1)), [CNOT(Qubit(0), Qubit(1))]), + (CR(Qubit(2), Qubit(3), Float(2.123)), [CR(Qubit(2), Qubit(3), Float(2.123))]), + (I(Qubit(0)), []), + (X(Qubit(0)), [S(Qubit(0)), Ry(Qubit(0), Float(math.pi)), Sdag(Qubit(0))]), + (Rx(Qubit(0), Float(0.9)), [S(Qubit(0)), Ry(Qubit(0), Float(0.9)), Sdag(Qubit(0))]), + (Y(Qubit(0)), [Ry(Qubit(0), Float(math.pi))]), + (Ry(Qubit(0), Float(0.9)), [Ry(Qubit(0), Float(0.9))]), + (Z(Qubit(0)), [Rz(Qubit(0), Float(math.pi))]), + (Rz(Qubit(0), Float(0.123)), [Rz(Qubit(0), Float(0.123))]), + (H(Qubit(0)), [Rz(Qubit(0), Float(math.pi)), Ry(Qubit(0), Float(math.pi / 2))]), + ( + BlochSphereRotation(qubit=Qubit(0), angle=5.21, axis=(1, 2, 3), phase=0.324), + [ + Rz(Qubit(0), Float(0.018644578210710527)), + Ry(Qubit(0), Float(-0.6209410696845807)), + Rz(Qubit(0), Float(-0.9086506397909061)), + ], + ), + ], + ids=["CNOT", "CR", "I", "X", "Rx", "Y", "Ry", "Z", "Rz", "H", "arbitrary"], +) +def test_zyz_decomposer(decomposer: ZYZDecomposer, gate: Gate, expected_result: list[Gate]) -> None: + assert decomposer.decompose(gate) == expected_result From ac9ecf01c11deccc716a388bd88eb187abb679a2 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Tue, 4 Jun 2024 17:27:26 +0200 Subject: [PATCH 13/14] Update tests --- .../test_quantify_scheduler_exporter.py | 27 ++++++------- test/ir_equality_test_base.py | 40 +++++-------------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/test/exporter/test_quantify_scheduler_exporter.py b/test/exporter/test_quantify_scheduler_exporter.py index 87b98008..225c1ab5 100644 --- a/test/exporter/test_quantify_scheduler_exporter.py +++ b/test/exporter/test_quantify_scheduler_exporter.py @@ -1,9 +1,10 @@ import contextlib import importlib.util import math -import unittest import unittest.mock +import pytest + from opensquirrel.circuit import Circuit from opensquirrel.common import ATOL 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): self._stack.__exit__(exc_type, exc_value, exc_traceback) -class QuantifySchedulerExporterTest(unittest.TestCase): +class TestQuantifySchedulerExporter: def test_export(self): register_manager = RegisterManager(qubit_register_size=3) ir = IR() @@ -68,15 +69,15 @@ def test_export(self): ) mock_quantify_scheduler_gates.CZ.assert_called_once_with(qC="q[0]", qT="q[1]") mock_quantify_scheduler_gates.Rz.assert_called_once_with(theta=FloatEq(math.degrees(2.34)), qubit="q[1]") - self.assertEqual(mock_schedule.add.call_count, 7) + assert mock_schedule.add.call_count == 7 - def check_gate_not_supported(self, g: Gate): + def check_gate_not_supported(self, g: Gate) -> None: register_manager = RegisterManager(qubit_register_size=3) ir = IR() ir.add_gate(g) with MockedQuantifyScheduler(): - with self.assertRaisesRegex(Exception, "Cannot exporter circuit: it contains unsupported gates"): + with pytest.raises(Exception, match="Cannot exporter circuit: it contains unsupported gates"): quantify_scheduler_exporter.export(Circuit(register_manager, ir)) def test_gates_not_supported(self): @@ -86,12 +87,10 @@ def test_gates_not_supported(self): self.check_gate_not_supported(CCZ(Qubit(0), Qubit(1), Qubit(2))) -class QuantifySchedulerNotInstalledTest(unittest.TestCase): - @unittest.skipIf( - importlib.util.find_spec("quantify_scheduler") is not None, reason="quantify_scheduler is installed" - ) - def test_quantify_scheduler_not_installed(self): - with self.assertRaisesRegex( - Exception, "quantify-scheduler is not installed, or cannot be installed on your system" - ): - quantify_scheduler_exporter.export(unittest.mock.MagicMock()) +@pytest.mark.skipif( + importlib.util.find_spec("quantify_scheduler") is not None, reason="quantify_scheduler is installed" +) +def test_quantify_scheduler_not_installed() -> None: + empty_circuit = Circuit(RegisterManager(1), IR()) + with pytest.raises(Exception, match="quantify-scheduler is not installed, or cannot be installed on your system"): + quantify_scheduler_exporter.export(empty_circuit) diff --git a/test/ir_equality_test_base.py b/test/ir_equality_test_base.py index 264c85d3..ceb3f6b4 100644 --- a/test/ir_equality_test_base.py +++ b/test/ir_equality_test_base.py @@ -1,40 +1,22 @@ -import unittest +from __future__ import annotations -from opensquirrel import circuit_matrix_calculator -from opensquirrel.circuit import Circuit -from opensquirrel.common import are_matrices_equivalent_up_to_global_phase - - -class IREqualityTestBase(unittest.TestCase): - def check_equivalence_up_to_global_phase(self, matrix_a, matrix_b): - self.assertTrue(are_matrices_equivalent_up_to_global_phase(matrix_a, matrix_b)) - - def modify_circuit_and_check(self, circuit, action, expected_circuit=None): - """ - Checks whether the action preserves: - - the number of qubits, - - the qubit register name(s), - - the circuit matrix up to a global phase factor. - """ - # Store matrix before decompositions. - expected_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) +from collections.abc import Callable +from typing import Any - action(circuit) +import numpy as np +from numpy.typing import NDArray - # Get matrix after decompositions. - actual_matrix = circuit_matrix_calculator.get_circuit_matrix(circuit) - - self.check_equivalence_up_to_global_phase(actual_matrix, expected_matrix) - - if expected_circuit is not None: - self.assertEqual(circuit, expected_circuit) +from opensquirrel import Circuit, circuit_matrix_calculator +from opensquirrel.common import are_matrices_equivalent_up_to_global_phase -def check_equivalence_up_to_global_phase(matrix_a, matrix_b): +def check_equivalence_up_to_global_phase(matrix_a: NDArray[np.complex_], matrix_b: NDArray[np.complex_]) -> None: assert are_matrices_equivalent_up_to_global_phase(matrix_a, matrix_b) -def modify_circuit_and_check(circuit, action, expected_circuit=None): +def modify_circuit_and_check( + circuit: Circuit, action: Callable[[Circuit, Any]], expected_circuit: Circuit | None = None +): """ Checks whether the action preserves: - the number of qubits, From 3be6252ea34528f231bb58172e7e3c506db4c1f5 Mon Sep 17 00:00:00 2001 From: Stan van der Linde Date: Wed, 5 Jun 2024 09:28:12 +0200 Subject: [PATCH 14/14] Python38 compatibility --- test/test_zyz_decomposer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_zyz_decomposer.py b/test/test_zyz_decomposer.py index d9846de6..1fccaf49 100644 --- a/test/test_zyz_decomposer.py +++ b/test/test_zyz_decomposer.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math import pytest