diff --git a/CHANGELOG.md b/CHANGELOG.md index b8a9aa145..fa01b5469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## v1.76.0 (2024-04-01) + +### Features + + * add support for OpenQASM measure on a subset of qubits + +### Bug Fixes and Other Changes + + * restore the dependent test back to pennylane + +### Documentation Changes + + * fix GPI2 gate matrix representation + ## v1.75.0 (2024-03-28) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 9c4e62904..2721571fe 100644 --- a/src/braket/_sdk/_version.py +++ b/src/braket/_sdk/_version.py @@ -15,4 +15,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "1.75.1.dev0" +__version__ = "1.76.1.dev0" diff --git a/src/braket/circuits/braket_program_context.py b/src/braket/circuits/braket_program_context.py index 46d6e3b0b..a17864ed4 100644 --- a/src/braket/circuits/braket_program_context.py +++ b/src/braket/circuits/braket_program_context.py @@ -18,6 +18,7 @@ from braket.circuits import Circuit, Instruction from braket.circuits.gates import Unitary +from braket.circuits.measure import Measure from braket.circuits.noises import Kraus from braket.circuits.translations import ( BRAKET_GATES, @@ -159,3 +160,12 @@ def handle_parameter_value( return evaluated_value return FreeParameterExpression(evaluated_value) return value + + def add_measure(self, target: tuple[int]) -> None: + """Add a measure instruction to the circuit + + Args: + target (tuple[int]): the target qubits to be measured. + """ + instruction = Instruction(Measure(), list(target)) + self._circuit.add_instruction(instruction) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 3f4918a1f..a4b48629d 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -13,6 +13,7 @@ from __future__ import annotations +from collections import Counter from collections.abc import Callable, Iterable from numbers import Number from typing import Any, Optional, TypeVar, Union @@ -26,6 +27,7 @@ from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction +from braket.circuits.measure import Measure from braket.circuits.moments import Moments, MomentType from braket.circuits.noise import Noise from braket.circuits.noise_helpers import ( @@ -148,6 +150,7 @@ def __init__(self, addable: AddableTypes | None = None, *args, **kwargs): self._parameters = set() self._observables_simultaneously_measurable = True self._has_compiler_directives = False + self._measure_targets = None if addable is not None: self.add(addable, *args, **kwargs) @@ -273,6 +276,7 @@ def add_result_type( Raises: TypeError: If both `target_mapping` and `target` are supplied. + ValueError: If a meaure instruction exists on the current circuit. Examples: >>> result_type = ResultType.Probability(target=[0, 1]) @@ -298,6 +302,12 @@ def add_result_type( if target_mapping and target is not None: raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.") + if self._measure_targets: + raise ValueError( + "cannot add a result type to a circuit which already contains a " + "measure instruction." + ) + if not target_mapping and not target: # Nothing has been supplied, add result_type result_type_to_add = result_type @@ -407,6 +417,46 @@ def _add_to_qubit_observable_set(self, result_type: ResultType) -> None: if isinstance(result_type, ObservableResultType) and result_type.target: self._qubit_observable_set.update(result_type.target) + def _check_if_qubit_measured( + self, + instruction: Instruction, + target: QubitSetInput | None = None, + target_mapping: dict[QubitInput, QubitInput] | None = None, + ) -> None: + """Checks if the target qubits are measured. If the qubit is already measured + the instruction will not be added to the Circuit. + + Args: + instruction (Instruction): `Instruction` to add into `self`. + target (QubitSetInput | None): Target qubits for the + `instruction`. If a single qubit gate, an instruction is created for every index + in `target`. + Default = `None`. + target_mapping (dict[QubitInput, QubitInput] | None): A dictionary of + qubit mappings to apply to the `instruction.target`. Key is the qubit in + `instruction.target` and the value is what the key will be changed to. + Default = `None`. + + Raises: + ValueError: If adding a gate or noise operation after a measure instruction. + """ + if ( + # check if there is a measure instruction on the target qubit + target + and target in self._measure_targets + # check if there is a measure instruction on any qubits in the target_mapping + or (target_mapping and any(targ in self._measure_targets for targ in target_mapping)) + # If no target or target_mapping is supplied, check if there is a measure + # instruction on the current instructions target qubit + or ( + instruction.target + and any(targ in self._measure_targets for targ in instruction.target) + ) + ): + raise ValueError( + "cannot add a gate or noise operation on a qubit after a measure instruction." + ) + def add_instruction( self, instruction: Instruction, @@ -431,6 +481,7 @@ def add_instruction( Raises: TypeError: If both `target_mapping` and `target` are supplied. + ValueError: If adding a gate or noise after a measure instruction. Examples: >>> instr = Instruction(Gate.CNot(), [0, 1]) @@ -458,6 +509,10 @@ def add_instruction( if target_mapping and target is not None: raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.") + # Check if there is a measure instruction on the circuit + if not isinstance(instruction.operator, Measure) and self._measure_targets: + self._check_if_qubit_measured(instruction, target, target_mapping) + if not target_mapping and not target: # Nothing has been supplied, add instruction instructions_to_add = [instruction] @@ -635,6 +690,9 @@ def add_verbatim_box( if verbatim_circuit.result_types: raise ValueError("Verbatim subcircuit is not measured and cannot have result types") + if verbatim_circuit._measure_targets: + raise ValueError("cannot measure a subcircuit inside a verbatim box.") + if verbatim_circuit.instructions: self.add_instruction(Instruction(compiler_directives.StartVerbatimBox())) for instruction in verbatim_circuit.instructions: @@ -643,6 +701,99 @@ def add_verbatim_box( self._has_compiler_directives = True return self + def _add_measure(self, target_qubits: QubitSetInput) -> None: + """Adds a measure instruction to the the circuit + + Args: + target_qubits (QubitSetInput): target qubits to measure. + """ + for idx, target in enumerate(target_qubits): + num_qubits_measured = ( + len(self._measure_targets) + if self._measure_targets and len(target_qubits) == 1 + else 0 + ) + self.add_instruction( + Instruction( + operator=Measure(index=idx + num_qubits_measured), + target=target, + ) + ) + if self._measure_targets: + self._measure_targets.append(target) + else: + self._measure_targets = [target] + + def measure(self, target_qubits: QubitSetInput | None = None) -> Circuit: + """ + Add a `measure` operator to `self` ensuring only the target qubits are measured. + + Args: + target_qubits (QubitSetInput | None): target qubits to measure. + Default=None + + Returns: + Circuit: self + + Raises: + IndexError: If `self` has no qubits. + IndexError: If target qubits are not within the range of the current circuit. + ValueError: If the current circuit contains any result types. + ValueError: If the target qubit is already measured. + + Examples: + >>> circ = Circuit.h(0).cnot(0, 1).measure([0]) + >>> circ.print(list(circ.instructions)) + [Instruction('operator': H('qubit_count': 1), 'target': QubitSet([Qubit(0)]), + Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(0), + Qubit(1)]), + Instruction('operator': H('qubit_count': 1), 'target': QubitSet([Qubit(2)]), + Instruction('operator': Measure, 'target': QubitSet([Qubit(0)])] + """ + # check whether measuring an empty circuit + if not self.qubits: + raise IndexError("cannot measure an empty circuit.") + + if isinstance(target_qubits, int): + target_qubits = [target_qubits] + + # Check if result types are added on the circuit + if self.result_types: + raise ValueError("a circuit cannot contain both measure instructions and result types.") + + if target_qubits: + # Check if the target_qubits are already measured + if self._measure_targets and any( + target in self._measure_targets for target in target_qubits + ): + intersection = set(target_qubits) & set(self._measure_targets) + raise ValueError( + f"cannot measure the same qubit(s) {', '.join(map(str, intersection))} " + "more than once." + ) + # Check if there are repeated qubits in the same measurement + if len(target_qubits) != len(set(target_qubits)): + intersection = [ + qubit for qubit, count in Counter(target_qubits).items() if count > 1 + ] + raise ValueError( + f"cannot repeat qubit(s) {', '.join(map(str, intersection))} " + "in the same measurement." + ) + self._add_measure(target_qubits=target_qubits) + else: + # Check if any qubits are already measured + if self._measure_targets: + intersection = set(self.qubits) & set(self._measure_targets) + raise ValueError( + f"cannot measure the same qubit(s) {', '.join(map(str, intersection))} " + "more than once." + ) + # Measure all the qubits + self._add_measure(target_qubits=self.qubits) + + return self + def apply_gate_noise( self, noise: Union[type[Noise], Iterable[type[Noise]]], @@ -1208,7 +1359,8 @@ def _to_openqasm( for result_type in self.result_types ] ) - else: + # measure all the qubits if a measure instruction is not provided + elif self._measure_targets is None: qubits = ( sorted(self.qubits) if serialization_properties.qubit_reference_type == QubitReferenceType.VIRTUAL @@ -1230,7 +1382,12 @@ def _create_openqasm_header( for parameter in self.parameters: ir_instructions.append(f"input float {parameter};") if not self.result_types: - ir_instructions.append(f"bit[{self.qubit_count}] b;") + bit_count = ( + len(self._measure_targets) + if self._measure_targets is not None + else self.qubit_count + ) + ir_instructions.append(f"bit[{bit_count}] b;") if serialization_properties.qubit_reference_type == QubitReferenceType.VIRTUAL: total_qubits = max(self.qubits).real + 1 diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index df17c03c3..b93b2dfa7 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -3301,7 +3301,7 @@ class GPi2(AngledGate): Unitary matrix: - .. math:: \mathtt{GPi2}(\phi) = \begin{bmatrix} + .. math:: \mathtt{GPi2}(\phi) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -i e^{-i \phi} \\ -i e^{i \phi} & 1 \end{bmatrix}. @@ -3351,7 +3351,7 @@ def gpi2( ) -> Iterable[Instruction]: r"""IonQ GPi2 gate. - .. math:: \mathtt{GPi2}(\phi) = \begin{bmatrix} + .. math:: \mathtt{GPi2}(\phi) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -i e^{-i \phi} \\ -i e^{i \phi} & 1 \end{bmatrix}. diff --git a/src/braket/circuits/measure.py b/src/braket/circuits/measure.py new file mode 100644 index 000000000..d31555ba9 --- /dev/null +++ b/src/braket/circuits/measure.py @@ -0,0 +1,99 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from __future__ import annotations + +from typing import Any + +from braket.circuits.quantum_operator import QuantumOperator +from braket.circuits.serialization import ( + IRType, + OpenQASMSerializationProperties, + SerializationProperties, +) +from braket.registers.qubit_set import QubitSet + + +class Measure(QuantumOperator): + """Class `Measure` represents a measure operation on targeted qubits""" + + def __init__(self, **kwargs): + """Inits a `Measure`. + + Raises: + ValueError: `qubit_count` is less than 1, `ascii_symbols` are `None`, or + `ascii_symbols` length != `qubit_count` + """ + super().__init__(qubit_count=1, ascii_symbols=["M"]) + self._target_index = kwargs.get("index") + + @property + def ascii_symbols(self) -> tuple[str]: + """tuple[str]: Returns the ascii symbols for the measure.""" + return self._ascii_symbols + + def to_ir( + self, + target: QubitSet | None = None, + ir_type: IRType = IRType.OPENQASM, + serialization_properties: SerializationProperties | None = None, + **kwargs, + ) -> Any: + """Returns IR object of the measure operator. + + Args: + target (QubitSet | None): target qubit(s). Defaults to None + ir_type(IRType) : The IRType to use for converting the measure object to its + IR representation. Defaults to IRType.OpenQASM. + serialization_properties (SerializationProperties | None): The serialization properties + to use while serializing the object to the IR representation. The serialization + properties supplied must correspond to the supplied `ir_type`. Defaults to None. + + Returns: + Any: IR object of the measure operator. + + Raises: + ValueError: If the supplied `ir_type` is not supported. + """ + if ir_type == IRType.JAQCD: + return self._to_jaqcd() + elif ir_type == IRType.OPENQASM: + return self._to_openqasm( + target, serialization_properties or OpenQASMSerializationProperties() ** kwargs + ) + else: + raise ValueError(f"supplied ir_type {ir_type} is not supported.") + + def _to_jaqcd(self) -> Any: + """Returns the JAQCD representation of the measure.""" + raise NotImplementedError("measure instructions are not supported with JAQCD.") + + def _to_openqasm( + self, target: QubitSet, serialization_properties: OpenQASMSerializationProperties + ) -> str: + """Returns the openqasm string representation of the measure.""" + target_qubits = [serialization_properties.format_target(int(qubit)) for qubit in target] + instructions = [] + for idx, qubit in enumerate(target_qubits): + bit_index = ( + self._target_index if self._target_index and len(target_qubits) == 1 else idx + ) + instructions.append(f"b[{bit_index}] = measure {qubit};") + + return "\n".join(instructions) + + def __eq__(self, other: Measure): + return isinstance(other, Measure) + + def __repr__(self): + return self.name diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index 64a3556c1..031c2f5e5 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -21,6 +21,7 @@ from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction +from braket.circuits.measure import Measure from braket.circuits.noise import Noise from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet @@ -34,6 +35,7 @@ class MomentType(str, Enum): INITIALIZATION_NOISE: a initialization noise channel READOUT_NOISE: a readout noise channel COMPILER_DIRECTIVE: an instruction to the compiler, external to the quantum program itself + MEASURE: a measurement """ GATE = "gate" @@ -43,6 +45,7 @@ class MomentType(str, Enum): READOUT_NOISE = "readout_noise" COMPILER_DIRECTIVE = "compiler_directive" GLOBAL_PHASE = "global_phase" + MEASURE = "measure" class MomentsKey(NamedTuple): @@ -191,6 +194,14 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._number_gphase_in_current_moment, ) self._moments[key] = instruction + elif isinstance(operator, Measure): + qubit_range = instruction.target.union(instruction.control) + time = self._get_qubit_times(self._max_times.keys()) + 1 + self._moments[MomentsKey(time, qubit_range, MomentType.MEASURE, noise_index)] = ( + instruction + ) + self._qubits.update(qubit_range) + self._depth = max(self._depth, time + 1) else: qubit_range = instruction.target.union(instruction.control) time = self._update_qubit_times(qubit_range) diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py index 9b85c30e1..83763a9ae 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py @@ -20,6 +20,7 @@ from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction +from braket.circuits.measure import Measure from braket.circuits.moments import MomentType from braket.circuits.noise import Noise from braket.circuits.result_type import ResultType @@ -129,9 +130,9 @@ def _group_items( """ groupings = [] for item in items: - # Can only print Gate and Noise operators for instructions at the moment + # Can only print Gate, Noise and Measure operators for instructions at the moment if isinstance(item, Instruction) and not isinstance( - item.operator, (Gate, Noise, CompilerDirective) + item.operator, (Gate, Noise, CompilerDirective, Measure) ): continue diff --git a/src/braket/tasks/gate_model_quantum_task_result.py b/src/braket/tasks/gate_model_quantum_task_result.py index dec914ba7..b64d4e009 100644 --- a/src/braket/tasks/gate_model_quantum_task_result.py +++ b/src/braket/tasks/gate_model_quantum_task_result.py @@ -286,11 +286,6 @@ def _from_object_internal_computational_basis_sampling( " the result obj", ) measured_qubits = result.measuredQubits - if len(measured_qubits) != measurements.shape[1]: - raise ValueError( - f"Measured qubits {measured_qubits} is not equivalent to number of qubits " - + f"{measurements.shape[1]} in measurements" - ) if result.resultTypes: # Jaqcd does not return anything in the resultTypes schema field since the # result types are easily parsable from the IR. However, an OpenQASM program diff --git a/test/integ_tests/test_measure.py b/test/integ_tests/test_measure.py new file mode 100644 index 000000000..b7fef275b --- /dev/null +++ b/test/integ_tests/test_measure.py @@ -0,0 +1,85 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import re + +import pytest +from botocore.exceptions import ClientError + +from braket.aws.aws_device import AwsDevice +from braket.circuits.circuit import Circuit +from braket.devices import LocalSimulator + +DEVICE = LocalSimulator() +SHOTS = 8000 + +IONQ_ARN = "arn:aws:braket:us-east-1::device/qpu/ionq/Harmony" +SIMULATOR_ARN = "arn:aws:braket:::device/quantum-simulator/amazon/sv1" +OQC_ARN = "arn:aws:braket:eu-west-2::device/qpu/oqc/Lucy" + + +@pytest.mark.parametrize("arn", [(IONQ_ARN), (SIMULATOR_ARN)]) +def test_unsupported_devices(arn): + device = AwsDevice(arn) + if device.status == "OFFLINE": + pytest.skip("Device offline") + + circ = Circuit().h(0).cnot(0, 1).h(2).measure([0, 1]) + error_string = re.escape( + "An error occurred (ValidationException) when calling the " + "CreateQuantumTask operation: Device requires all qubits in the program to be measured. " + "This may be caused by declaring non-contiguous qubits or measuring partial qubits" + ) + with pytest.raises(ClientError, match=error_string): + device.run(circ, shots=1000) + + +@pytest.mark.parametrize("sim", [("braket_sv"), ("braket_dm")]) +def test_measure_on_local_sim(sim): + circ = Circuit().h(0).cnot(0, 1).h(2).measure([0, 1]) + device = LocalSimulator(sim) + result = device.run(circ, SHOTS).result() + assert len(result.measurements[0]) == 2 + assert result.measured_qubits == [0, 1] + + +@pytest.mark.parametrize("arn", [(OQC_ARN)]) +def test_measure_on_supported_devices(arn): + device = AwsDevice(arn) + if not device.is_available: + pytest.skip("Device offline") + circ = Circuit().h(0).cnot(0, 1).measure([0]) + result = device.run(circ, SHOTS).result() + assert len(result.measurements[0]) == 1 + assert result.measured_qubits == [0] + + +@pytest.mark.parametrize( + "circuit, expected_measured_qubits", + [ + (Circuit().h(0).cnot(0, 1).cnot(1, 2).cnot(2, 3).measure([0, 1, 3]), [0, 1, 3]), + (Circuit().h(0).measure(0), [0]), + ], +) +def test_measure_targets(circuit, expected_measured_qubits): + result = DEVICE.run(circuit, SHOTS).result() + assert result.measured_qubits == expected_measured_qubits + assert len(result.measurements[0]) == len(expected_measured_qubits) + + +def test_measure_with_noise(): + device = LocalSimulator("braket_dm") + circuit = Circuit().x(0).x(1).bit_flip(0, probability=0.1).measure(0) + result = device.run(circuit, SHOTS).result() + assert result.measured_qubits == [0] + assert len(result.measurements[0]) == 1 diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 88e32d92f..7724d9077 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -872,3 +872,66 @@ def __init__(self): "T : | 0 | 1 | 2 | 3 | 4 |", ) _assert_correct_diagram(circ, expected) + + +def test_measure(): + circ = Circuit().h(0).cnot(0, 1).measure([0]) + expected = ( + "T : |0|1|2|", + " ", + "q0 : -H-C-M-", + " | ", + "q1 : ---X---", + "", + "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) + + +def test_measure_multiple_instructions_after(): + circ = ( + Circuit() + .h(0) + .cnot(0, 1) + .cnot(1, 2) + .cnot(2, 3) + .measure(0) + .measure(1) + .h(3) + .cnot(3, 4) + .measure([2, 3]) + ) + expected = ( + "T : |0|1|2|3|4|5|6|", + " ", + "q0 : -H-C-----M-----", + " | ", + "q1 : ---X-C---M-----", + " | ", + "q2 : -----X-C-----M-", + " | ", + "q3 : -------X-H-C-M-", + " | ", + "q4 : -----------X---", + "", + "T : |0|1|2|3|4|5|6|", + ) + _assert_correct_diagram(circ, expected) diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index ecaad12bf..7680f9b4f 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -35,6 +35,8 @@ observables, ) from braket.circuits.gate_calibrations import GateCalibrations +from braket.circuits.measure import Measure +from braket.circuits.noises import BitFlip from braket.circuits.parameterizable import Parameterizable from braket.circuits.serialization import ( IRType, @@ -570,6 +572,245 @@ def test_add_verbatim_box_result_types(): ) +def test_measure(): + circ = Circuit().h(0).cnot(0, 1).measure([0]) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .add_instruction(Instruction(Gate.CNot(), [0, 1])) + .add_instruction(Instruction(Measure(), 0)) + ) + assert circ == expected + + +def test_measure_int(): + circ = Circuit().h(0).cnot(0, 1).measure(0) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .add_instruction(Instruction(Gate.CNot(), [0, 1])) + .add_instruction(Instruction(Measure(), 0)) + ) + assert circ == expected + + +def test_measure_multiple_targets(): + circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).cnot(2, 3).measure([0, 1, 3]) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .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)) + ) + assert circ == expected + assert circ._measure_targets == [0, 1, 3] + + +def test_measure_with_noise(): + circ = Circuit().x(0).x(1).bit_flip(0, probability=0.1).measure(0) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.X(), 0)) + .add_instruction(Instruction(Gate.X(), 1)) + .add_instruction(Instruction(BitFlip(probability=0.1), 0)) + .add_instruction(Instruction(Measure(), 0)) + ) + assert circ == expected + + +def test_measure_verbatim_box(): + circ = Circuit().add_verbatim_box(Circuit().x(0).x(1)).measure(0) + expected = ( + Circuit() + .add_instruction(Instruction(compiler_directives.StartVerbatimBox())) + .add_instruction(Instruction(Gate.X(), 0)) + .add_instruction(Instruction(Gate.X(), 1)) + .add_instruction(Instruction(compiler_directives.EndVerbatimBox())) + .add_instruction(Instruction(Measure(), 0)) + ) + expected_ir = OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[2] q;", + "#pragma braket verbatim", + "box{", + "x q[0];", + "x q[1];", + "}", + "b[0] = measure q[0];", + ] + ), + inputs={}, + ) + assert circ == expected + assert circ.to_ir("OPENQASM") == expected_ir + + +def test_measure_in_verbatim_subcircuit(): + 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)) + + +def test_measure_qubits_out_of_range(): + circ = Circuit().h(0).cnot(0, 1).measure(4) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .add_instruction(Instruction(Gate.CNot(), [0, 1])) + .add_instruction(Instruction(Measure(), 4)) + ) + assert circ == expected + + +def test_measure_empty_circuit(): + with pytest.raises(IndexError): + Circuit().measure() + + +def test_measure_no_target(): + 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(), 0)) + .add_instruction(Instruction(Measure(), 1)) + ) + assert circ == expected + + +def test_measure_with_result_types(): + 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) + + +def test_result_type_with_measure(): + message = "cannot add a result type to a circuit which already contains a measure instruction." + with pytest.raises(ValueError, match=message): + Circuit().h(0).measure(0).sample(observable=Observable.Z(), target=0) + + +def test_measure_with_multiple_measures(): + circ = Circuit().h(0).cnot(0, 1).h(2).measure([0, 1]).measure(2) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .add_instruction(Instruction(Gate.CNot(), [0, 1])) + .add_instruction(Instruction(Gate.H(), 2)) + .add_instruction(Instruction(Measure(), 0)) + .add_instruction(Instruction(Measure(), 1)) + .add_instruction(Instruction(Measure(), 2)) + ) + assert circ == expected + + +def test_measure_same_qubit_twice(): + message = "cannot measure the same qubit\\(s\\) 0 more than once." + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).measure(0).measure(1).measure(0) + + +def test_measure_same_qubit_twice_with_list(): + message = "cannot measure the same qubit\\(s\\) 0 more than once." + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).measure(0).measure([0, 1]) + + +def test_measure_same_qubit_twice_with_one_measure(): + message = "cannot repeat qubit\\(s\\) 0 in the same measurement." + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).measure([0, 0, 0]) + + +def test_measure_empty_measure_after_measure_with_targets(): + message = "cannot measure the same qubit\\(s\\) 0, 1 more than once." + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).cnot(1, 2).measure(0).measure(1).measure() + + +def test_measure_gate_after(): + message = "cannot add a gate or noise operation on a qubit after a measure instruction." + with pytest.raises(ValueError, match=message): + Circuit().h(0).measure(0).h([0, 1]) + + +def test_measure_gate_after_with_target_mapping(): + message = "cannot add a gate or noise operation on a qubit after a measure instruction." + instr = Instruction(Gate.CNot(), [0, 1]) + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).cnot(1, 2).measure([0, 1]).add_instruction( + instr, target_mapping={0: 10, 1: 11} + ) + + +def test_measure_gate_after_with_target(): + message = "cannot add a gate or noise operation on a qubit after a measure instruction." + instr = Instruction(Gate.CNot(), [0, 1]) + with pytest.raises(ValueError, match=message): + Circuit().h(0).cnot(0, 1).cnot(1, 2).measure([0, 1]).add_instruction(instr, target=[10, 11]) + + +def test_measure_gate_after_measurement(): + circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).measure(0).h(2) + expected = ( + Circuit() + .add_instruction(Instruction(Gate.H(), 0)) + .add_instruction(Instruction(Gate.CNot(), [0, 1])) + .add_instruction(Instruction(Gate.CNot(), [1, 2])) + .add_instruction(Instruction(Measure(), 0)) + .add_instruction(Instruction(Gate.H(), 2)) + ) + assert circ == expected + + +def test_to_ir_with_measure(): + circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).measure([0, 2]) + expected_ir = OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[2] b;", + "qubit[3] q;", + "h q[0];", + "cnot q[0], q[1];", + "cnot q[1], q[2];", + "b[0] = measure q[0];", + "b[1] = measure q[2];", + ] + ), + inputs={}, + ) + assert circ.to_ir("OPENQASM") == expected_ir + + +def test_from_ir_with_measure(): + ir = OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[3] q;", + "h q[0];", + "cnot q[0], q[1];", + "cnot q[1], q[2];", + "b[0] = measure q[0];", + "b[1] = measure q[2];", + ] + ), + inputs={}, + ) + expected_circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).measure(0).measure(2) + assert Circuit.from_ir(source=ir.source, inputs=ir.inputs) == expected_circ + + def test_add_with_instruction_with_default(cnot_instr): circ = Circuit().add(cnot_instr) assert circ == Circuit().add_instruction(cnot_instr) @@ -1268,7 +1509,7 @@ def foo( "expected_circuit, ir", [ ( - Circuit().h(0, control=1, control_state=0), + Circuit().h(0, control=1, control_state=0).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1284,7 +1525,7 @@ def foo( ), ), ( - Circuit().cnot(target=0, control=1), + Circuit().cnot(target=0, control=1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1300,7 +1541,7 @@ def foo( ), ), ( - Circuit().x(0, control=[1], control_state=[0]), + Circuit().x(0, control=[1], control_state=[0]).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1316,7 +1557,7 @@ def foo( ), ), ( - Circuit().rx(0, 0.15, control=1, control_state=1), + Circuit().rx(0, 0.15, control=1, control_state=1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1332,7 +1573,7 @@ def foo( ), ), ( - Circuit().ry(0, 0.2, control=1, control_state=1), + Circuit().ry(0, 0.2, control=1, control_state=1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1348,7 +1589,7 @@ def foo( ), ), ( - Circuit().rz(0, 0.25, control=[1], control_state=[0]), + Circuit().rz(0, 0.25, control=[1], control_state=[0]).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1364,7 +1605,7 @@ def foo( ), ), ( - Circuit().s(target=0, control=[1], control_state=[0]), + Circuit().s(target=0, control=[1], control_state=[0]).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1380,7 +1621,7 @@ def foo( ), ), ( - Circuit().t(target=1, control=[0], control_state=[0]), + Circuit().t(target=1, control=[0], control_state=[0]).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1396,7 +1637,7 @@ def foo( ), ), ( - Circuit().cphaseshift(target=0, control=1, angle=0.15), + Circuit().cphaseshift(target=0, control=1, angle=0.15).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1412,7 +1653,7 @@ def foo( ), ), ( - Circuit().ccnot(*[0, 1], target=2), + Circuit().ccnot(*[0, 1], target=2).measure(0).measure(1).measure(2), OpenQasmProgram( source="\n".join( [ @@ -1499,7 +1740,7 @@ def foo( ), ), ( - Circuit().bit_flip(0, 0.1), + Circuit().bit_flip(0, 0.1).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1514,7 +1755,7 @@ def foo( ), ), ( - Circuit().generalized_amplitude_damping(0, 0.1, 0.1), + Circuit().generalized_amplitude_damping(0, 0.1, 0.1).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1529,7 +1770,7 @@ def foo( ), ), ( - Circuit().phase_flip(0, 0.2), + Circuit().phase_flip(0, 0.2).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1544,7 +1785,7 @@ def foo( ), ), ( - Circuit().depolarizing(0, 0.5), + Circuit().depolarizing(0, 0.5).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1559,7 +1800,7 @@ def foo( ), ), ( - Circuit().amplitude_damping(0, 0.8), + Circuit().amplitude_damping(0, 0.8).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1574,7 +1815,7 @@ def foo( ), ), ( - Circuit().phase_damping(0, 0.1), + Circuit().phase_damping(0, 0.1).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1606,7 +1847,12 @@ def foo( Circuit() .rx(0, 0.15, control=2, control_state=0) .rx(1, 0.3, control=[2, 3]) - .cnot(target=0, control=[2, 3, 4]), + .cnot(target=0, control=[2, 3, 4]) + .measure(0) + .measure(1) + .measure(2) + .measure(3) + .measure(4), OpenQasmProgram( source="\n".join( [ @@ -1627,7 +1873,17 @@ def foo( ), ), ( - Circuit().cnot(0, 1).cnot(target=2, control=3).cnot(target=4, control=[5, 6]), + Circuit() + .cnot(0, 1) + .cnot(target=2, control=3) + .cnot(target=4, control=[5, 6]) + .measure(0) + .measure(1) + .measure(2) + .measure(3) + .measure(4) + .measure(5) + .measure(6), OpenQasmProgram( source="\n".join( [ @@ -1650,7 +1906,7 @@ def foo( ), ), ( - Circuit().h(0, power=-2.5).h(0, power=0), + Circuit().h(0, power=-2.5).h(0, power=0).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1666,7 +1922,7 @@ def foo( ), ), ( - Circuit().unitary(matrix=np.array([[0, 1], [1, 0]]), targets=[0]), + Circuit().unitary(matrix=np.array([[0, 1], [1, 0]]), targets=[0]).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1681,7 +1937,7 @@ def foo( ), ), ( - Circuit().pauli_channel(0, probX=0.1, probY=0.2, probZ=0.3), + Circuit().pauli_channel(0, probX=0.1, probY=0.2, probZ=0.3).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1696,7 +1952,7 @@ def foo( ), ), ( - Circuit().two_qubit_depolarizing(0, 1, probability=0.1), + Circuit().two_qubit_depolarizing(0, 1, probability=0.1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1712,7 +1968,7 @@ def foo( ), ), ( - Circuit().two_qubit_dephasing(0, 1, probability=0.1), + Circuit().two_qubit_dephasing(0, 1, probability=0.1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1728,7 +1984,7 @@ def foo( ), ), ( - Circuit().two_qubit_dephasing(0, 1, probability=0.1), + Circuit().two_qubit_dephasing(0, 1, probability=0.1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1787,13 +2043,15 @@ def foo( ), ), ( - Circuit().kraus( + Circuit() + .kraus( [0], matrices=[ np.array([[0.9486833j, 0], [0, 0.9486833j]]), np.array([[0, 0.31622777], [0.31622777, 0]]), ], - ), + ) + .measure(0), OpenQasmProgram( source="\n".join( [ @@ -1810,7 +2068,7 @@ def foo( ), ), ( - Circuit().rx(0, FreeParameter("theta")), + Circuit().rx(0, FreeParameter("theta")).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1826,7 +2084,7 @@ def foo( ), ), ( - Circuit().rx(0, np.pi), + Circuit().rx(0, np.pi).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1841,7 +2099,7 @@ def foo( ), ), ( - Circuit().rx(0, 2 * np.pi), + Circuit().rx(0, 2 * np.pi).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1856,7 +2114,7 @@ def foo( ), ), ( - Circuit().gphase(0.15).x(0), + Circuit().gphase(0.15).x(0).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1879,7 +2137,7 @@ def test_from_ir(expected_circuit, ir): def test_from_ir_inputs_updated(): - circuit = Circuit().rx(0, 0.2).ry(0, 0.1) + circuit = Circuit().rx(0, 0.2).ry(0, 0.1).measure(0) openqasm = OpenQasmProgram( source="\n".join( [ @@ -1902,7 +2160,7 @@ def test_from_ir_inputs_updated(): "expected_circuit, ir", [ ( - Circuit().h(0).cnot(0, 1), + Circuit().h(0).cnot(0, 1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1922,7 +2180,7 @@ def test_from_ir_inputs_updated(): ), ), ( - Circuit().h(0).h(1), + Circuit().h(0).h(1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1942,7 +2200,7 @@ def test_from_ir_inputs_updated(): ), ), ( - Circuit().h(0).h(1).cnot(0, 1), + Circuit().h(0).h(1).cnot(0, 1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1961,7 +2219,7 @@ def test_from_ir_inputs_updated(): ), ), ( - Circuit().h(0).h(1).cnot(0, 1), + Circuit().h(0).h(1).cnot(0, 1).measure(0).measure(1), OpenQasmProgram( source="\n".join( [ @@ -1980,7 +2238,7 @@ def test_from_ir_inputs_updated(): ), ), ( - Circuit().x(0), + Circuit().x(0).measure(0), OpenQasmProgram( source="\n".join( [ @@ -1998,7 +2256,7 @@ def test_from_ir_inputs_updated(): ), ), ( - Circuit().rx(0, FreeParameter("theta")).rx(0, 2 * FreeParameter("theta")), + Circuit().rx(0, FreeParameter("theta")).rx(0, 2 * FreeParameter("theta")).measure(0), OpenQasmProgram( source="\n".join( [ diff --git a/test/unit_tests/braket/circuits/test_measure.py b/test/unit_tests/braket/circuits/test_measure.py new file mode 100644 index 000000000..8911da5e4 --- /dev/null +++ b/test/unit_tests/braket/circuits/test_measure.py @@ -0,0 +1,100 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +import pytest + +from braket.circuits.measure import Measure +from braket.circuits.quantum_operator import QuantumOperator +from braket.circuits.serialization import ( + IRType, + OpenQASMSerializationProperties, + QubitReferenceType, +) + + +@pytest.fixture +def measure(): + return Measure() + + +def test_is_operator(measure): + assert isinstance(measure, QuantumOperator) + + +def test_equality(): + measure1 = Measure() + measure2 = Measure() + non_measure = "non measure" + + assert measure1 == measure2 + assert measure1 is not measure2 + assert measure1 != non_measure + + +def test_ascii_symbols(measure): + assert measure.ascii_symbols == ("M",) + + +def test_str(measure): + assert str(measure) == measure.name + + +@pytest.mark.parametrize( + "ir_type, serialization_properties, expected_exception, expected_message", + [ + ( + 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."), + ], +) +def test_measure_to_ir( + ir_type, serialization_properties, expected_exception, expected_message, measure +): + with pytest.raises(expected_exception) as exc: + measure.to_ir(ir_type=ir_type, serialization_properties=serialization_properties) + assert exc.value.args[0] == expected_message + + +@pytest.mark.parametrize( + "measure, target, serialization_properties, expected_ir", + [ + ( + Measure(), + [0], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.VIRTUAL), + "b[0] = measure q[0];", + ), + ( + Measure(), + [1, 4], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL), + "\n".join( + [ + "b[0] = measure $1;", + "b[1] = measure $4;", + ] + ), + ), + ], +) +def test_measure_to_ir_openqasm(measure, target, serialization_properties, expected_ir): + assert ( + measure.to_ir( + target, ir_type=IRType.OPENQASM, serialization_properties=serialization_properties + ) + == expected_ir + ) diff --git a/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py b/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py index 96df634cb..f78b4e746 100644 --- a/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py @@ -1019,3 +1019,81 @@ def __init__(self): "T : │ 0 │", ) _assert_correct_diagram(circ, expected) + + +def test_measure(): + 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) + + +def test_measure_with_multiple_measures(): + circ = Circuit().h(0).cnot(0, 1).cnot(1, 2).cnot(2, 3).measure([0, 2]).measure(3).measure(1) + expected = ( + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ H ├───●───────────────┤ M ├─", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ", + "q1 : ───────┤ X ├───●─────────┤ M ├─", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ", + "q2 : ─────────────┤ X ├───●───┤ M ├─", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ", + "q3 : ───────────────────┤ X ├─┤ M ├─", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + ) + _assert_correct_diagram(circ, expected) + _assert_correct_diagram(circ, expected) + + +def test_measure_multiple_instructions_after(): + circ = ( + Circuit() + .h(0) + .cnot(0, 1) + .cnot(1, 2) + .cnot(2, 3) + .measure(0) + .measure(1) + .h(3) + .cnot(3, 4) + .measure([2, 3]) + ) + expected = ( + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ H ├───●───────────────┤ M ├─────────────", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ", + "q1 : ───────┤ X ├───●─────────┤ M ├─────────────", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ", + "q2 : ─────────────┤ X ├───●───────────────┤ M ├─", + " └───┘ │ └───┘ ", + " ┌─┴─┐ ┌───┐ ┌───┐ ", + "q3 : ───────────────────┤ X ├─┤ H ├───●───┤ M ├─", + " └───┘ └───┘ │ └───┘ ", + " ┌─┴─┐ ", + "q4 : ───────────────────────────────┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + ) + _assert_correct_diagram(circ, expected) diff --git a/test/unit_tests/braket/tasks/test_gate_model_quantum_task_result.py b/test/unit_tests/braket/tasks/test_gate_model_quantum_task_result.py index 8c0f8d9a9..43dd06db7 100644 --- a/test/unit_tests/braket/tasks/test_gate_model_quantum_task_result.py +++ b/test/unit_tests/braket/tasks/test_gate_model_quantum_task_result.py @@ -262,16 +262,6 @@ def malformatted_results_1(task_metadata_shots, additional_metadata): ).json() -@pytest.fixture -def malformatted_results_2(task_metadata_shots, additional_metadata): - return GateModelTaskResult( - measurementProbabilities={"011000": 0.9999999999999982}, - measuredQubits=[0], - taskMetadata=task_metadata_shots, - additionalMetadata=additional_metadata, - ).json() - - @pytest.fixture def openqasm_result_obj_shots(task_metadata_shots, additional_metadata_openqasm): return GateModelTaskResult.construct( @@ -484,11 +474,6 @@ def test_shots_no_measurements_no_measurement_probs(malformatted_results_1): GateModelQuantumTaskResult.from_string(malformatted_results_1) -@pytest.mark.xfail(raises=ValueError) -def test_measurements_measured_qubits_mismatch(malformatted_results_2): - GateModelQuantumTaskResult.from_string(malformatted_results_2) - - @pytest.mark.parametrize("ir_result,expected_result", test_ir_results) def test_calculate_ir_results(ir_result, expected_result): ir_string = jaqcd.Program(