Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
162e759
add the barrier instruction
nilsquet Aug 28, 2025
cb08560
adjust both diagram printer
nilsquet Aug 28, 2025
5216984
added respective tests
nilsquet Aug 28, 2025
7c2109d
improved diagram tests
nilsquet Aug 28, 2025
dec4c31
removed unnecessary comments
nilsquet Aug 28, 2025
b8eb1d3
improved the formatting through linter
nilsquet Aug 29, 2025
563b8ff
removed the equivalence checking function
nilsquet Sep 2, 2025
6b6ce7e
adjusted the qubit range handling in moments
nilsquet Sep 2, 2025
1859ea6
Merge branch 'main' into feature/barrier-instruction
nilsquet Sep 2, 2025
e696ffd
removed unnecessary comments
nilsquet Sep 2, 2025
85d5f8d
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
3149693
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
a8cf258
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
541af99
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
d2f635a
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
57bdcec
refactored the circuit diagram drawer
nilsquet Sep 2, 2025
9b0f12b
Merge branch 'main' into feature/barrier-instruction
nilsquet Sep 3, 2025
29dfa62
feat: removed unnecessary method
nilsquet Sep 4, 2025
c372558
feat: changed the ascii symbol handling
nilsquet Sep 4, 2025
dc3213d
feat: adjusted the moments to be able to handle the different barrier…
nilsquet Sep 4, 2025
8999bb0
feat: went back to the initial ascii during init
nilsquet Sep 4, 2025
6c3dc58
feat: adjusted both the ASCII and unicode drawer to be able to work w…
nilsquet Sep 4, 2025
e9d8b99
feat: adjusted the group handling for the drawers
nilsquet Sep 4, 2025
cedd3ab
test: adjusted test to changed ASCII init
nilsquet Sep 4, 2025
98326aa
feature: added more comprehensive tests
nilsquet Sep 4, 2025
d182c12
fix: linting
nilsquet Sep 4, 2025
d1ea852
fix: removed unnecessary string prefix
nilsquet Sep 4, 2025
8db5a31
fix: refactoring due to linter warnings
nilsquet Sep 4, 2025
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
23 changes: 23 additions & 0 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,29 @@ def add_verbatim_box(
self._has_compiler_directives = True
return self

def barrier(self, target: QubitSetInput | None = None) -> Circuit:
"""Add a barrier compiler directive to the circuit.

Args:
target (QubitSetInput | None): Target qubits for the barrier.
If None, applies to all qubits in the circuit.

Returns:
Circuit: self

Examples:
>>> circ = Circuit().h(0).barrier([0, 1]).cnot(0, 1)
>>> circ = Circuit().h(0).h(1).barrier() # barrier on all qubits
"""
target_qubits = self.qubits if target is None else QubitSet(target)

if target_qubits:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because self.qubits can be None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation was to not have any effect when adding a barrier to an empty circuit.

self.add_instruction(
Instruction(compiler_directives.Barrier(list(target_qubits)), target=target_qubits)
)
self._has_compiler_directives = True
return self

def _add_measure(self, target_qubits: QubitSetInput) -> None:
"""Adds a measure instruction to the the circuit

Expand Down
38 changes: 38 additions & 0 deletions src/braket/circuits/compiler_directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import braket.ir.jaqcd as ir

from braket.circuits.compiler_directive import CompilerDirective
from braket.circuits.serialization import IRType, SerializationProperties
from braket.registers.qubit_set import QubitSet


class StartVerbatimBox(CompilerDirective):
Expand Down Expand Up @@ -60,3 +62,39 @@ def _to_jaqcd(self, *args, **kwargs) -> Any:

def _to_openqasm(self) -> str:
return "}"


class Barrier(CompilerDirective):
r"""Barrier compiler directive."""

def __init__(self, qubit_indices: list[int]):
super().__init__(["||"])
self._qubit_indices = qubit_indices

@property
def qubit_indices(self) -> list[int]:
return self._qubit_indices

@property
def qubit_count(self) -> int:
return len(self._qubit_indices)

def _to_jaqcd(self) -> Any:
raise NotImplementedError("Barrier is not supported in JAQCD")

def to_ir(
self,
target: QubitSet | None,
ir_type: IRType,
serialization_properties: SerializationProperties | None = None,
**kwargs,
) -> Any:
if ir_type.name == "OPENQASM":
if target:
qubits = ", ".join(serialization_properties.format_target(int(q)) for q in target)
return f"barrier {qubits};"
return "barrier;"
return super().to_ir(target, ir_type, serialization_properties, **kwargs)

def _to_openqasm(self) -> str:
return "barrier"
8 changes: 6 additions & 2 deletions src/braket/circuits/moments.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,12 @@ def add(self, instructions: Iterable[Instruction] | Instruction, noise_index: in
def _add(self, instruction: Instruction, noise_index: int = 0) -> None:
operator = instruction.operator
if isinstance(operator, CompilerDirective):
time = self._update_qubit_times(self._qubits)
self._moments[MomentsKey(time, None, MomentType.COMPILER_DIRECTIVE, 0)] = instruction
qubit_range = instruction.target.union(instruction.control or QubitSet())
time = self._update_qubit_times(qubit_range or self._qubits)
self._moments[MomentsKey(time, qubit_range, MomentType.COMPILER_DIRECTIVE, 0)] = (
instruction
)
self._qubits.update(qubit_range)
self._depth = time + 1
self._time_all_qubits = time
elif isinstance(operator, Noise):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ def _duplicate_time_at_bottom(cls, lines: str) -> None:
# duplicate times after an empty line
lines.append(lines[0])

@classmethod
def _get_compiler_directive_symbols(
cls, item: Instruction, circuit_qubits: QubitSet
) -> list[str]:
"""Get symbols for compiler directives, handling barriers specially."""
if item.operator.name == "Barrier":
return item.ascii_symbols
ascii_symbol = item.ascii_symbols[0]
marker = "*" * len(ascii_symbol)
num_after = len(circuit_qubits) - 1
after = ["|"] * (num_after - 1) + ([marker] if num_after else [])
return [ascii_symbol, *after]

@classmethod
def _create_diagram_column(
cls,
Expand Down Expand Up @@ -102,11 +115,7 @@ def _create_diagram_column(
control_qubits = QubitSet()
target_and_control = target_qubits.union(control_qubits)
qubits = circuit_qubits
ascii_symbol = item.ascii_symbols[0]
marker = "*" * len(ascii_symbol)
num_after = len(circuit_qubits) - 1
after = ["|"] * (num_after - 1) + ([marker] if num_after else [])
ascii_symbols = [ascii_symbol, *after]
ascii_symbols = cls._get_compiler_directive_symbols(item, circuit_qubits)
elif (
isinstance(item, Instruction)
and isinstance(item.operator, Gate)
Expand Down Expand Up @@ -135,9 +144,9 @@ def _create_diagram_column(
# Determine if the qubit is part of the item or in the middle of a
# multi qubit item.
if qubit in target_qubits:
item_qubit_index = [ # noqa: RUF015
item_qubit_index = next(
index for index, q in enumerate(target_qubits) if q == qubit
][0]
)
power_string = (
f"^{power}"
if (
Expand All @@ -150,10 +159,19 @@ def _create_diagram_column(
)
else ""
)
idx = (
0
if (
isinstance(item, Instruction)
and isinstance(item.operator, CompilerDirective)
and item.operator.name == "Barrier"
)
else item_qubit_index
)
symbols[qubit] = (
f"({ascii_symbols[item_qubit_index]}{power_string})"
f"({ascii_symbols[idx]}{power_string})"
if power_string
else ascii_symbols[item_qubit_index]
else ascii_symbols[idx]
)
elif qubit in control_qubits:
symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,12 @@ def _draw_symbol(
"""
top = ""
bottom = ""
if symbol in {"C", "N", "SWAP"}:
if symbol in {"C", "N", "SWAP", "||"}:
if connection in {"above", "both"}:
top = _fill_symbol(cls._vertical_delimiter(), " ")
if connection in {"below", "both"}:
bottom = _fill_symbol(cls._vertical_delimiter(), " ")
new_symbol = {"C": "●", "N": "◯", "SWAP": "x"}
new_symbol = {"C": "●", "N": "◯", "SWAP": "x", "||": "▒"}
# replace SWAP by x
# the size of the moment remains as if there was a box with 4 characters inside
symbol = _fill_symbol(new_symbol[symbol], cls._qubit_line_character())
Expand Down
30 changes: 30 additions & 0 deletions test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,3 +956,33 @@ def test_measure_with_readout_noise():
"T : |0| 1 |2|",
)
_assert_correct_diagram(circ, expected)


def test_barrier_circuit_visualization_without_other_gates():
circ = Circuit().barrier(target=[0, 100])
expected = (
"T : |0 |",
" ",
"q0 : -||-",
" | ",
"q100 : -||-",
"",
"T : |0 |",
)
_assert_correct_diagram(circ, expected)


def test_barrier_circuit_visualization_with_other_gates():
circ = Circuit().x(0).barrier(target=[0, 100]).h(3)
expected = (
"T : |0|1 |2|",
" ",
"q0 : -X-||---",
" | ",
"q3 : ---||-H-",
" | ",
"q100 : ---||---",
"",
"T : |0|1 |2|",
)
_assert_correct_diagram(circ, expected)
47 changes: 47 additions & 0 deletions test/unit_tests/braket/circuits/test_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3589,3 +3589,50 @@ def test_from_ir_round_trip_transformation_with_targeted_measurements():

assert Circuit.from_ir(ir) == Circuit.from_ir(circuit.to_ir("OPENQASM"))
assert circuit.to_ir("OPENQASM") == Circuit.from_ir(ir).to_ir("OPENQASM")


def test_barrier_specific_qubits():
circ = Circuit().barrier([0, 1, 2])
assert len(circ.instructions) == 1
instr = circ.instructions[0]
assert isinstance(instr.operator, compiler_directives.Barrier)
assert instr.target == QubitSet([0, 1, 2])
assert instr.operator.qubit_indices == [0, 1, 2]
assert circ.qubits_frozen is True


def test_barrier_all_qubits():
circ = Circuit().h(0).h(1).barrier()
assert len(circ.instructions) == 3
barrier_instr = circ.instructions[2]
assert isinstance(barrier_instr.operator, compiler_directives.Barrier)
assert barrier_instr.target == QubitSet([0, 1])


def test_barrier_empty_circuit():
circ = Circuit().barrier()
assert len(circ.instructions) == 0 # No barrier added to empty circuit


def test_barrier_none_target():
circ = Circuit().h(0).h(2).barrier(None)
barrier_instr = circ.instructions[2]
assert barrier_instr.target == QubitSet([0, 2])


def test_barrier_openqasm_export_specific_qubits():
circ = Circuit().h(0).barrier([0, 1]).cnot(0, 1)
qasm = circ.to_ir(IRType.OPENQASM).source
assert "barrier q[0], q[1];" in qasm


def test_barrier_openqasm_export_all_qubits():
circ = Circuit().h(0).h(1).barrier().cnot(0, 1)
qasm = circ.to_ir(IRType.OPENQASM).source
assert "barrier q[0], q[1];" in qasm


def test_barrier_jaqcd_export_fails():
circ = Circuit().h(0).barrier([0, 1])
with pytest.raises(NotImplementedError, match="Barrier is not supported in JAQCD"):
circ.to_ir(IRType.JAQCD)
20 changes: 20 additions & 0 deletions test/unit_tests/braket/circuits/test_compiler_directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from braket.circuits import compiler_directives
from braket.circuits.compiler_directive import CompilerDirective
from braket.circuits.serialization import IRType
from braket.circuits.serialization import OpenQASMSerializationProperties, QubitReferenceType


@pytest.mark.parametrize(
Expand Down Expand Up @@ -49,3 +50,22 @@ def test_verbatim(testclass, irclass, openqasm_str, counterpart):
assert directive is not op
assert directive != CompilerDirective(ascii_symbols=["foo"])
assert directive != "not a directive"


def test_barrier():
barrier = compiler_directives.Barrier([0, 1, 2])
assert barrier.qubit_indices == [0, 1, 2]
assert barrier.qubit_count == 3
assert barrier.ascii_symbols == ("||",)
assert barrier._to_openqasm() == "barrier"
assert repr(barrier) == "Barrier"

with pytest.raises(NotImplementedError, match="Barrier is not supported in JAQCD"):
barrier._to_jaqcd()

props = OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.VIRTUAL)
result = barrier.to_ir([0, 1, 2], IRType.OPENQASM, props)
assert result == "barrier q[0], q[1], q[2];"

result = barrier.to_ir([], IRType.OPENQASM, props)
assert result == "barrier;"
33 changes: 33 additions & 0 deletions test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -1119,3 +1119,36 @@ def test_measure_with_readout_noise():
"T : │ 0 │ 1 │ 2 │",
)
_assert_correct_diagram(circ, expected)


def test_barrier_circuit_visualization_without_other_gates():
circ = Circuit().barrier(target=[0, 100])
expected = (
"T : │ 0 │",
" ",
"q0 : ───▒────",
" │ ",
" │ ",
"q100 : ───▒────",
" ",
"T : │ 0 │",
)
_assert_correct_diagram(circ, expected)


def test_barrier_circuit_visualization_with_other_gates():
circ = Circuit().x(0).barrier(target=[0, 100]).h(3)
expected = (
"T : │ 0 │ 1 │ 2 │",
" ┌───┐ ",
"q0 : ─┤ X ├───▒──────────",
" └───┘ │ ",
" │ ┌───┐ ",
"q3 : ─────────▒────┤ H ├─",
" │ └───┘ ",
" │ ",
"q100 : ─────────▒──────────",
" ",
"T : │ 0 │ 1 │ 2 │",
)
_assert_correct_diagram(circ, expected)
Loading