Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]

Expand Down
15 changes: 15 additions & 0 deletions opensquirrel/circuit_builder.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.

Expand Down
81 changes: 81 additions & 0 deletions tests/test_circuit_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Loading