Skip to content

Commit

Permalink
Update error messages, add diagram tests, and update measure to measu…
Browse files Browse the repository at this point in the history
…re all qubits when no targets provided
  • Loading branch information
ashlhans committed Mar 22, 2024
1 parent 6276332 commit 3265a48
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 43 deletions.
33 changes: 22 additions & 11 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ def add_verbatim_box(
raise ValueError("Verbatim subcircuit is not measured and cannot have result types")

if verbatim_circuit._measure_targets:
raise ValueError("Verbatim subcircuit is not measured.")
raise ValueError("Cannot measure a subcircuit inside a verbatim box.")

if verbatim_circuit.instructions:
self.add_instruction(Instruction(compiler_directives.StartVerbatimBox()))
Expand All @@ -651,7 +651,7 @@ def add_verbatim_box(
self._has_compiler_directives = True
return self

def measure(self, target_qubits: np.ndarray | int = []) -> Circuit:
def measure(self, target_qubits: np.ndarray | int = None) -> Circuit:
"""
Add a `measure` operator to `self` ensuring only the target qubits are measured.
Expand Down Expand Up @@ -682,28 +682,39 @@ def measure(self, target_qubits: np.ndarray | int = []) -> Circuit:
target_qubits = [target_qubits]

# Check that the target qubits are on the circuit
if not all(qubit in self.qubits for qubit in target_qubits):
if target_qubits and not all(qubit in self.qubits for qubit in target_qubits):
raise IndexError("Target qubits must be within the range of the current circuit.")

# Check if result types are added on the circuit
if self.result_types:
raise ValueError("Cannot perform a measure instruction with a result type.")
raise ValueError("a circuit cannot contain both measure instructions and result types.")

# Check if there is more than one measure instruction
if any(isinstance(instruction.operator, Measure) for instruction in self.instructions):
raise ValueError("Cannot perform more than one measure instruction.")

if target_qubits:
for idx, target in enumerate(target_qubits):
self.add_instruction(
Instruction(
operator=Measure(qubit_count=1, ascii_symbols=["M"], index=idx),
target=target,
)
self.add_instruction(
Instruction(
operator=Measure(
qubit_count=len(target_qubits), ascii_symbols=["M"] * len(target_qubits)
),
target=target_qubits,
)
)
self._measure_targets = target_qubits
else:
raise ValueError("Measure must include one or more target qubits.")
# Measure all the qubits
self.add_instruction(
Instruction(
operator=Measure(
qubit_count=self.qubit_count, ascii_symbols=["M"] * self.qubit_count
),
target=self.qubits,
)
)
self._measure_targets = self.qubits

return self

def apply_gate_noise(
Expand Down
15 changes: 4 additions & 11 deletions src/braket/circuits/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
class Measure(QuantumOperator):
"""Class `Measure` represents a measure operation on targeted qubits"""

def __init__(
self, qubit_count: Optional[int] = 1, ascii_symbols: Sequence[str] = ["M"], **kwargs
):
def __init__(self, qubit_count: Optional[int] = 1, ascii_symbols: Sequence[str] = ["M"]):
"""Inits a `Measure`.
Args:
Expand All @@ -44,11 +42,6 @@ def __init__(
`ascii_symbols` length != `qubit_count`
"""
super().__init__(qubit_count=qubit_count, ascii_symbols=ascii_symbols)
self._target_index = kwargs.get("index")

@property
def name(self) -> str:
return self.__class__.__name__

@property
def ascii_symbols(self) -> tuple[str, ...]:
Expand Down Expand Up @@ -89,7 +82,7 @@ def to_ir(

def _to_jaqcd(self) -> Any:
"""Returns the JAQCD representation of the measure."""
raise NotImplementedError("to_jaqcd has not been implemented yet.")
raise NotImplementedError("Measure instructions are not supported with JAQCD.")

def _to_openqasm(
self, target: QubitSet, serialization_properties: OpenQASMSerializationProperties
Expand All @@ -98,12 +91,12 @@ def _to_openqasm(
target_qubits = [serialization_properties.format_target(int(qubit)) for qubit in target]
instructions = ""
for idx, qubit in enumerate(target_qubits):
instructions += f"b[{self._target_index}] = measure {qubit};"
instructions += f"b[{idx}] = measure {qubit};"

return instructions

def __eq__(self, other: Measure):
return isinstance(other, Measure) and self.name == other.name
return isinstance(other, Measure) and self.qubit_count == other.qubit_count

def __repr__(self):
return self.name
2 changes: 0 additions & 2 deletions test/integ_tests/test_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ def test_measure_on_supported_devices(arn):
pytest.skip("Device offline")
circ = Circuit().h(0).cnot(0, 1).measure([0])
result = device.run(circ, SHOTS).result()
print(result)
assert len(result.measurements[0]) == 1
assert result.measured_qubits == [0]

Expand All @@ -74,7 +73,6 @@ def test_measure_on_supported_devices(arn):
)
def test_measure_targets(circuit, expected_measured_qubits):
result = DEVICE.run(circuit, SHOTS).result()
print(result)
assert result.measured_qubits == expected_measured_qubits
assert len(result.measurements[0]) == len(expected_measured_qubits)

Expand Down
18 changes: 18 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 @@ -886,3 +886,21 @@ def test_measure():
"T : |0|1|2|",
)
_assert_correct_diagram(circ, expected)


def test_measure_multiple_targets():
circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).cnot(2, 3).measure([0, 2, 3])
expected = (
"T : |0|1|2|3|4|",
" ",
"q0 : -H-C-----M-",
" | | ",
"q1 : ---X-C---|-",
" | | ",
"q2 : -----X-C-M-",
" | | ",
"q3 : -------X-M-",
"",
"T : |0|1|2|3|4|",
)
_assert_correct_diagram(circ, expected)
21 changes: 13 additions & 8 deletions test/unit_tests/braket/circuits/test_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,9 @@ def test_measure_multiple_targets():
.add_instruction(Instruction(Gate.CNot(), [0, 1]))
.add_instruction(Instruction(Gate.CNot(), [1, 2]))
.add_instruction(Instruction(Gate.CNot(), [2, 3]))
.add_instruction(Instruction(Measure(), 0))
.add_instruction(Instruction(Measure(), 1))
.add_instruction(Instruction(Measure(), 3))
.add_instruction(
Instruction(Measure(qubit_count=3, ascii_symbols=["M", "M", "M"]), [0, 1, 3])
)
)
assert circ == expected
assert circ._measure_targets == [0, 1, 3]
Expand Down Expand Up @@ -653,7 +653,7 @@ def test_measure_verbatim_box():


def test_measure_in_verbatim_subcircuit():
message = "Verbatim subcircuit is not measured."
message = "Cannot measure a subcircuit inside a verbatim box."
with pytest.raises(ValueError, match=message):
Circuit().add_verbatim_box(Circuit().x(0).x(1).measure(0))

Expand All @@ -669,13 +669,18 @@ def test_measure_empty_circuit():


def test_measure_no_target():
message = "Measure must include one or more target qubits."
with pytest.raises(ValueError, match=message):
Circuit().h(0).cnot(0, 1).measure()
circ = Circuit().h(0).cnot(0, 1).measure()
expected = (
Circuit()
.add_instruction(Instruction(Gate.H(), 0))
.add_instruction(Instruction(Gate.CNot(), [0, 1]))
.add_instruction(Instruction(Measure(qubit_count=2, ascii_symbols=["M", "M"]), [0, 1]))
)
assert circ == expected


def test_measure_with_result_types():
message = "Cannot perform a measure instruction with a result type."
message = "a circuit cannot contain both measure instructions and result types."
with pytest.raises(ValueError, match=message):
Circuit().h(0).sample(observable=Observable.Z(), target=0).measure(0)

Expand Down
22 changes: 11 additions & 11 deletions test/unit_tests/braket/circuits/test_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def ascii_symbols():

@pytest.fixture
def measure(ascii_symbols):
return Measure(qubit_count=1, ascii_symbols=ascii_symbols, index=0)
return Measure(qubit_count=1, ascii_symbols=ascii_symbols)


def test_is_operator(measure):
Expand All @@ -55,19 +55,19 @@ def test_equality():
assert measure1 != non_measure


def test_name(measure):
expected = measure.__class__.__name__
assert measure.name == expected


def test_str(measure):
assert str(measure) == measure.name


@pytest.mark.parametrize(
"ir_type, serialization_properties, expected_exception, expected_message",
[
(IRType.JAQCD, None, NotImplementedError, "to_jaqcd has not been implemented yet."),
(
IRType.JAQCD,
None,
NotImplementedError,
"Measure instructions are not supported with JAQCD.",
),
("invalid-ir-type", None, ValueError, "Supplied ir_type invalid-ir-type is not supported."),
],
)
Expand All @@ -83,16 +83,16 @@ def test_measure_to_ir(
"measure, target, serialization_properties, expected_ir",
[
(
Measure(qubit_count=1, ascii_symbols=["M"], index=0),
Measure(),
[0],
OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.VIRTUAL),
"b[0] = measure q[0];",
),
(
Measure(qubit_count=1, ascii_symbols=["M"], index=1),
[4],
Measure(qubit_count=4, ascii_symbols=["M", "M", "M", "M"]),
[1, 4],
OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL),
"b[1] = measure $4;",
"b[0] = measure $1;b[1] = measure $4;",
),
],
)
Expand Down
21 changes: 21 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 @@ -1019,3 +1019,24 @@ def __init__(self):
"T : │ 0 │",
)
_assert_correct_diagram(circ, expected)


def test_measure():
circ = circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).cnot(2, 3).measure([0, 2, 3])
expected = (
"T : │ 0 │ 1 │ 2 │ 3 │ 4 │",
" ┌───┐ ┌───┐ ",
"q0 : ─┤ H ├───●───────────────┤ M ├─",
" └───┘ │ └─┬─┘ ",
" ┌─┴─┐ │ ",
"q1 : ───────┤ X ├───●───────────┼───",
" └───┘ │ │ ",
" ┌─┴─┐ ┌─┴─┐ ",
"q2 : ─────────────┤ X ├───●───┤ M ├─",
" └───┘ │ └─┬─┘ ",
" ┌─┴─┐ ┌─┴─┐ ",
"q3 : ───────────────────┤ X ├─┤ M ├─",
" └───┘ └───┘ ",
"T : │ 0 │ 1 │ 2 │ 3 │ 4 │",
)
_assert_correct_diagram(circ, expected)

0 comments on commit 3265a48

Please sign in to comment.