From d7f01db06367c43c021b65269e641342e92377bc Mon Sep 17 00:00:00 2001 From: Rares Oancea Date: Mon, 15 Jun 2026 09:22:26 +0200 Subject: [PATCH 1/2] add add_instruction to the circuit builder --- opensquirrel/circuit_builder.py | 15 ++++++ tests/test_circuit_builder.py | 81 +++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/opensquirrel/circuit_builder.py b/opensquirrel/circuit_builder.py index 860cbcda..47272c18 100644 --- a/opensquirrel/circuit_builder.py +++ b/opensquirrel/circuit_builder.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections import OrderedDict +from collections.abc import Iterable from copy import deepcopy from functools import partial from typing import Any @@ -190,6 +191,20 @@ def _expand_sgmq_args(self, attr: str, args: tuple[Any, ...]) -> list[tuple[Any, remaining_args = args[1:] return [(first, *remaining_args) for first in expanded_first] + def add_instruction(self, instruction: Instruction | Iterable[Instruction]) -> Self: + """Add instructions to the ir. + + Args: + instruction: A single instruction or an iterable of instructions to append to the IR. + + """ + if isinstance(instruction, Instruction): + instruction = [instruction] + for instr in instruction: + self._check_out_of_bounds_access(instr) + self.ir.add_statement(instr) + return self + def to_circuit(self) -> Circuit: """Build the circuit. diff --git a/tests/test_circuit_builder.py b/tests/test_circuit_builder.py index 9e262e40..3579267f 100644 --- a/tests/test_circuit_builder.py +++ b/tests/test_circuit_builder.py @@ -548,3 +548,84 @@ def test_sgmq_length_mismatch(self) -> None: with pytest.raises(ValueError, match="SGMQ requires matching operand lengths: got 3 and 2"): builder.CNOT([0, 1, 2], [1, 2]) + + +class TestAddInstruction: + def test_add_single_instruction(self) -> None: + builder = CircuitBuilder(2) + builder.add_instruction(H(0)) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [H(0)] + + def test_add_multiple_instructions(self) -> None: + builder = CircuitBuilder(2) + builder.add_instruction([H(0), CNOT(0, 1)]) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [H(0), CNOT(0, 1)] + + def test_add_multiple_parametric_instructions(self) -> None: + builder = CircuitBuilder(4) + builder.add_instruction(Rz(i, pi / 2 ** (i + 1)) for i in range(4)) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [Rz(i, pi / 2 ** (i + 1)) for i in range(4)] + + def test_add_instruction_chaining(self) -> None: + builder = CircuitBuilder(2) + circuit = builder.add_instruction(H(0)).add_instruction(CNOT(0, 1)).to_circuit() + + assert circuit.ir.statements == [H(0), CNOT(0, 1)] + + def test_add_instruction_mixed_with_plain_builder_calls(self) -> None: + builder = CircuitBuilder(3) + builder.H(0) + builder.add_instruction(CNOT(0, 1)) + builder.H(2) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [H(0), CNOT(0, 1), H(2)] + + def test_add_instruction_out_of_bounds_qubit(self) -> None: + builder = CircuitBuilder(2) + + with pytest.raises(IndexError, match="qubit index 5 is out of bounds"): + builder.add_instruction(H(5)) + + def test_add_instruction_out_of_bounds_bit(self) -> None: + builder = CircuitBuilder(2, 2) + + with pytest.raises(IndexError, match="bit index 10 is out of bounds"): + builder.add_instruction(Measure(0, 10)) + + def test_add_empty_list(self) -> None: + builder = CircuitBuilder(2) + builder.add_instruction([]) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [] + + @pytest.mark.parametrize( + ("instruction", "expected"), + [ + (Measure(0, 0), Measure(0, 0)), + (Init(0), Init(0)), + (Reset(0), Reset(0)), + (Barrier(0), Barrier(0)), + (Wait(0, 10), Wait(0, 10)), + ], + ids=[ + "measure", + "init", + "reset", + "barrier", + "wait", + ], + ) + def test_add_instruction_non_unitaries(self, instruction: Instruction, expected: Instruction) -> None: + builder = CircuitBuilder(2, 2) + builder.add_instruction(instruction) + circuit = builder.to_circuit() + + assert circuit.ir.statements == [expected] From 041be87d4c107050b7909d74f8c9dc80a29fdb35 Mon Sep 17 00:00:00 2001 From: Rares Oancea Date: Mon, 15 Jun 2026 10:45:21 +0200 Subject: [PATCH 2/2] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 287bfcc6..0a3f4b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ arbitrarily applied mapper pass. - `CircuitAnalyzer` analyzer pass for computing structural circuit metrics (size, interaction graph, gate dependency graph, density) - `Can2CZDecomposer` to decompose arbitrary two-qubit gates. - The following 2-qubit gates: `CV`, `CY`, `DCNOT`, `ECR`, `ISWAP`, `InvSqrtSWAP`, `M`, `MS`, `SqrtISWAP`, and `SqrtSWAP` +- Add `add_instruction` method to the `CircuitBuilder` ## [ 0.9.0 ] - [ 2025-12-19 ]