diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index b656763c3..0e75b5037 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -13,7 +13,6 @@ from __future__ import annotations -import warnings from collections import Counter from collections.abc import Callable, Iterable, Sequence from numbers import Number @@ -57,7 +56,9 @@ QubitReferenceType, SerializationProperties, ) -from braket.circuits.text_diagram_builders.unicode_circuit_diagram import UnicodeCircuitDiagram +from braket.circuits.text_diagram_builders.unicode_circuit_diagram import ( + UnicodeCircuitDiagram, +) from braket.circuits.unitary_calculation import calculate_unitary_big_endian from braket.pulse.ast.qasm_parser import ast_to_qasm from braket.pulse.frame import Frame @@ -67,7 +68,11 @@ from braket.registers.qubit_set import QubitSet, QubitSetInput SubroutineReturn = TypeVar( - "SubroutineReturn", Iterable[Instruction], Instruction, ResultType, Iterable[ResultType] + "SubroutineReturn", + Iterable[Instruction], + Instruction, + ResultType, + Iterable[ResultType], ) SubroutineCallable = TypeVar("SubroutineCallable", bound=Callable[..., SubroutineReturn]) AddableTypes = TypeVar("AddableTypes", SubroutineReturn, SubroutineCallable) @@ -153,6 +158,13 @@ def __init__(self, addable: AddableTypes | None = None, *args, **kwargs): if addable is not None: self.add(addable, *args, **kwargs) + def count(self, operator_name: str | None = None) -> int | Counter[str]: + """Count istructions in a circuit""" + count = Counter(instr.operator.name.lower() for instr in self.instructions) + if operator_name is not None: + return count[operator_name.lower()] + return count + @property def depth(self) -> int: """int: Get the circuit depth.""" @@ -1110,7 +1122,8 @@ def _use_parameter_value(self, param_values: dict[str, Number]) -> Circuit: if self._check_for_params(instruction): fixed_circ.add( Instruction( - instruction.operator.bind_values(**param_values), target=instruction.target + instruction.operator.bind_values(**param_values), + target=instruction.target, ) ) else: @@ -1312,7 +1325,7 @@ def diagram(self, circuit_diagram_class: type = UnicodeCircuitDiagram) -> str: def to_ir( self, - ir_type: IRType = IRType.OPENQASM, + ir_type: IRType = IRType.JAQCD, serialization_properties: SerializationProperties | None = None, gate_definitions: dict[tuple[Gate, QubitSet], PulseSequence] | None = None, ) -> OpenQasmProgram | JaqcdProgram: @@ -1338,10 +1351,6 @@ def to_ir( """ gate_definitions = gate_definitions or {} if ir_type == IRType.JAQCD: - warnings.warn( - "The JAQCD action type is deprecated. Please use OpenQASM 3 programs instead.", - stacklevel=2, - ) return self._to_jaqcd() if ir_type == IRType.OPENQASM: if serialization_properties and not isinstance( @@ -1411,7 +1420,8 @@ def _to_openqasm( openqasm_ir_type = IRType.OPENQASM ir_instructions.extend([ instruction.to_ir( - ir_type=openqasm_ir_type, serialization_properties=serialization_properties + ir_type=openqasm_ir_type, + serialization_properties=serialization_properties, ) for instruction in self.instructions ]) @@ -1419,7 +1429,8 @@ def _to_openqasm( if self.result_types: ir_instructions.extend([ result_type.to_ir( - ir_type=openqasm_ir_type, serialization_properties=serialization_properties + ir_type=openqasm_ir_type, + serialization_properties=serialization_properties, ) for result_type in self.result_types ]) @@ -1525,12 +1536,15 @@ def _generate_frame_wf_defcal_declarations( for param in calibration.parameters: self._parameters.add(param) arguments = [ - param._to_oqpy_expression() if isinstance(param, FreeParameter) else param + (param._to_oqpy_expression() if isinstance(param, FreeParameter) else param) for param in arguments ] with oqpy.defcal( - program, [oqpy.PhysicalQubits[int(k)] for k in qubits], gate_name, arguments + program, + [oqpy.PhysicalQubits[int(k)] for k in qubits], + gate_name, + arguments, ): program += calibration._program @@ -1622,7 +1636,11 @@ def _add_fixed_argument_calibrations( ) additional_calibrations[bound_key] = calibration(**{ p.name if isinstance(p, FreeParameterExpression) else p: v - for p, v in zip(gate.parameters, instruction.operator.parameters, strict=True) + for p, v in zip( + gate.parameters, + instruction.operator.parameters, + strict=True, + ) }) return additional_calibrations @@ -1760,7 +1778,9 @@ def subroutine(register: bool = False) -> Callable: Instruction('operator': 'H', 'target': QubitSet(Qubit(1),)) """ - def _subroutine_function_wrapper(func: Callable[..., SubroutineReturn]) -> SubroutineReturn: + def _subroutine_function_wrapper( + func: Callable[..., SubroutineReturn], + ) -> SubroutineReturn: if register: Circuit.register_subroutine(func) return func diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index aca5adc04..20a817843 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -15,7 +15,7 @@ import numpy as np import pytest - +from collections import Counter import braket.ir.jaqcd as jaqcd from braket.circuits import ( Circuit, @@ -44,7 +44,13 @@ ) from braket.circuits.translations import braket_result_to_result_type from braket.ir.openqasm import Program as OpenQasmProgram -from braket.pulse import DragGaussianWaveform, Frame, GaussianWaveform, Port, PulseSequence +from braket.pulse import ( + DragGaussianWaveform, + Frame, + GaussianWaveform, + Port, + PulseSequence, +) @pytest.fixture @@ -95,7 +101,11 @@ def port(): @pytest.fixture def predefined_frame_1(port): return Frame( - frame_id="predefined_frame_1", frequency=2e9, port=port, phase=0, is_predefined=True + frame_id="predefined_frame_1", + frequency=2e9, + port=port, + phase=0, + is_predefined=True, ) @@ -431,6 +441,18 @@ def test_add_result_type_same_observable_wrong_target_order_hermitian(): assert not circ.basis_rotation_instructions +def test_count_using_operator_name(): + circ = Circuit().h(0).cnot(0, 1).rx(1, 0).measure(0) + expected = 1 + assert circ.count("cnot") == expected + + +def test_count_without_operator_name(): + circ = Circuit().h(0).cnot(0, 1).rx(1, 0).measure(0) + expected = Counter({"h": 1, "cnot": 1, "rx": 1, "measure": 1}) + assert circ.count() == expected + + def test_add_result_type_with_target_and_mapping(prob): with pytest.raises(TypeError): Circuit().add_result_type(prob, target=[10], target_mapping={0: 10}) @@ -1184,9 +1206,9 @@ def h_nested(target): def test_ir_empty_instructions_result_types(): circ = Circuit() - with pytest.warns(UserWarning, match="JAQCD"): - ir = circ.to_ir(IRType.JAQCD) - assert ir == jaqcd.Program(instructions=[], results=[], basis_rotation_instructions=[]) + assert circ.to_ir() == jaqcd.Program( + instructions=[], results=[], basis_rotation_instructions=[] + ) def test_ir_non_empty_instructions_result_types(): @@ -1196,9 +1218,7 @@ def test_ir_non_empty_instructions_result_types(): results=[jaqcd.Probability(targets=[0, 1])], basis_rotation_instructions=[], ) - with pytest.warns(UserWarning, match="JAQCD"): - ir = circ.to_ir(IRType.JAQCD) - assert ir == expected + assert circ.to_ir() == expected def test_ir_non_empty_instructions_result_types_basis_rotation_instructions(): @@ -1208,16 +1228,7 @@ def test_ir_non_empty_instructions_result_types_basis_rotation_instructions(): results=[jaqcd.Sample(observable=["x"], targets=[0])], basis_rotation_instructions=[jaqcd.H(target=0)], ) - with pytest.warns(UserWarning, match="JAQCD"): - ir = circ.to_ir(IRType.JAQCD) - assert ir == expected - - -def test_to_ir_default_is_openqasm(): - """Calling Circuit.to_ir() with no ir_type argument should return an - OpenQASM program (after the JAQCD-deprecation default flip).""" - circ = Circuit().h(0).cnot(0, 1) - assert isinstance(circ.to_ir(), OpenQasmProgram) + assert circ.to_ir() == expected @pytest.mark.parametrize( @@ -1625,7 +1636,10 @@ def test_circuit_with_partial_calibrations(pulse_sequence_2): serialization_properties = OpenQASMSerializationProperties(QubitReferenceType.VIRTUAL) gate_calibrations = ( GateCalibrations({ - (gates.MS(-0.1, FreeParameter("beta"), -0.3), QubitSet([0, 1])): pulse_sequence_2 + ( + gates.MS(-0.1, FreeParameter("beta"), -0.3), + QubitSet([0, 1]), + ): pulse_sequence_2 }), ) circuit.to_ir( @@ -2591,7 +2605,10 @@ def test_to_unitary_with_global_phase(): (Circuit().y(1).z(2), np.kron(gates.Y().to_matrix(), gates.Z().to_matrix())), (Circuit().rx(1, 0.15), gates.Rx(0.15).to_matrix()), (Circuit().ry(1, 0.15).i(2), np.kron(gates.Ry(0.15).to_matrix(), np.eye(2))), - (Circuit().rz(1, 0.15).s(2), np.kron(gates.Rz(0.15).to_matrix(), gates.S().to_matrix())), + ( + Circuit().rz(1, 0.15).s(2), + np.kron(gates.Rz(0.15).to_matrix(), gates.S().to_matrix()), + ), (Circuit().pswap(1, 2, 0.15), gates.PSwap(0.15).to_matrix()), (Circuit().pswap(2, 1, 0.15), gates.PSwap(0.15).to_matrix()), (Circuit().xy(1, 2, 0.15).i(3), np.kron(gates.XY(0.15).to_matrix(), np.eye(2))), @@ -2604,18 +2621,30 @@ def test_to_unitary_with_global_phase(): (Circuit().ccnot(2, 1, 3), gates.CCNot().to_matrix()), (Circuit().cswap(1, 2, 3).i(4), np.kron(gates.CSwap().to_matrix(), np.eye(2))), (Circuit().cswap(1, 3, 2).i(4), np.kron(gates.CSwap().to_matrix(), np.eye(2))), - (Circuit().cswap(1, 2, 3).t(4), np.kron(gates.CSwap().to_matrix(), gates.T().to_matrix())), - (Circuit().cswap(1, 3, 2).t(4), np.kron(gates.CSwap().to_matrix(), gates.T().to_matrix())), + ( + Circuit().cswap(1, 2, 3).t(4), + np.kron(gates.CSwap().to_matrix(), gates.T().to_matrix()), + ), + ( + Circuit().cswap(1, 3, 2).t(4), + np.kron(gates.CSwap().to_matrix(), gates.T().to_matrix()), + ), (Circuit().h(0).h(0), gates.I().to_matrix()), (Circuit().h(0).x(0), np.dot(gates.X().to_matrix(), gates.H().to_matrix())), (Circuit().x(0).h(0), np.dot(gates.H().to_matrix(), gates.X().to_matrix())), ( Circuit().y(0).z(1).cnot(0, 1), - np.dot(gates.CNot().to_matrix(), np.kron(gates.Y().to_matrix(), gates.Z().to_matrix())), + np.dot( + gates.CNot().to_matrix(), + np.kron(gates.Y().to_matrix(), gates.Z().to_matrix()), + ), ), ( Circuit().z(0).y(1).cnot(0, 1), - np.dot(gates.CNot().to_matrix(), np.kron(gates.Z().to_matrix(), gates.Y().to_matrix())), + np.dot( + gates.CNot().to_matrix(), + np.kron(gates.Z().to_matrix(), gates.Y().to_matrix()), + ), ), ( Circuit().y(0).z(1).cnot(0, 1).cnot(1, 2), @@ -3003,7 +3032,8 @@ def test_basis_rotation_instructions_tensor_product(): .h(0) .cnot(0, 1) .expectation( - observable=observables.X() @ observables.Y() @ observables.Y(), target=[0, 1, 2] + observable=observables.X() @ observables.Y() @ observables.Y(), + target=[0, 1, 2], ) ) expected = [ @@ -3024,7 +3054,8 @@ def test_basis_rotation_instructions_tensor_product_shared_factors(): .h(0) .cnot(0, 1) .expectation( - observable=observables.X() @ observables.Y() @ observables.Y(), target=[0, 1, 2] + observable=observables.X() @ observables.Y() @ observables.Y(), + target=[0, 1, 2], ) .expectation(observable=observables.X() @ observables.Y(), target=[0, 1]) ) @@ -3101,7 +3132,10 @@ def test_basis_rotation_instructions_multiple_result_types_all_specified_same_ta .expectation(observable=observables.H()) .sample(observable=observables.H(), target=[0]) ) - expected = [Instruction(gates.Ry(-np.pi / 4), 0), Instruction(gates.Ry(-np.pi / 4), 1)] + expected = [ + Instruction(gates.Ry(-np.pi / 4), 0), + Instruction(gates.Ry(-np.pi / 4), 1), + ] assert circ.basis_rotation_instructions == expected @@ -3113,7 +3147,10 @@ def test_basis_rotation_instructions_multiple_result_types_specified_all_same_ta .sample(observable=observables.H(), target=[0]) .expectation(observable=observables.H()) ) - expected = [Instruction(gates.Ry(-np.pi / 4), 0), Instruction(gates.Ry(-np.pi / 4), 1)] + expected = [ + Instruction(gates.Ry(-np.pi / 4), 0), + Instruction(gates.Ry(-np.pi / 4), 1), + ] assert circ.basis_rotation_instructions == expected @@ -3122,9 +3159,13 @@ def test_basis_rotation_instructions_multiple_result_types_same_targets_hermitia Circuit() .h(0) .cnot(0, 1) - .sample(observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), target=[1]) + .sample( + observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), + target=[1], + ) .expectation( - observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), target=[1] + observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), + target=[1], ) ) expected = [Instruction(gates.Unitary(matrix=np.array([[0, 1], [1, 0]])), target=[1])] @@ -3136,9 +3177,13 @@ def test_basis_rotation_instructions_multiple_result_types_different_hermitian_t Circuit() .h(0) .cnot(0, 1) - .sample(observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), target=[1]) + .sample( + observable=observables.Hermitian(matrix=np.array([[1, 0], [0, -1]])), + target=[1], + ) .expectation( - observable=observables.Hermitian(matrix=np.array([[0, 1], [1, 0]])), target=[0] + observable=observables.Hermitian(matrix=np.array([[0, 1], [1, 0]])), + target=[0], ) ) expected = [ @@ -3168,7 +3213,8 @@ def test_basis_rotation_instructions_multiple_result_types_tensor_product_hermit target=[0, 1], ) .expectation( - observable=observables.Hermitian(matrix=np.array([[0, 1], [1, 0]])), target=[2] + observable=observables.Hermitian(matrix=np.array([[0, 1], [1, 0]])), + target=[2], ) ) expected = [ @@ -3192,7 +3238,8 @@ def test_basis_rotation_instructions_multiple_result_types_tensor_product_hermit .cnot(1, 2) .expectation(observable=observables.I(), target=[1]) .sample( - observable=observables.Hermitian(matrix=np.eye(4)) @ observables.H(), target=[0, 1, 2] + observable=observables.Hermitian(matrix=np.eye(4)) @ observables.H(), + target=[0, 1, 2], ) .variance(observable=observables.H(), target=[2]) .variance(observable=observables.Hermitian(matrix=np.eye(4)), target=[0, 1]) @@ -3212,7 +3259,10 @@ def test_basis_rotation_instructions_multiple_result_types_tensor_product_probab .cnot(0, 1) .cnot(1, 2) .probability([0, 1]) - .sample(observable=observables.Z() @ observables.Z() @ observables.H(), target=[0, 1, 2]) + .sample( + observable=observables.Z() @ observables.Z() @ observables.H(), + target=[0, 1, 2], + ) .variance(observable=observables.H(), target=[2]) ) expected = [ @@ -3568,7 +3618,8 @@ def test_pulse_circuit_to_openqasm(predefined_frame_1, user_defined_frame): ) pulse_sequence_2.play( - user_defined_frame, GaussianWaveform(length=1e-3, sigma=0.7, id="gauss_wf_ignore") + user_defined_frame, + GaussianWaveform(length=1e-3, sigma=0.7, id="gauss_wf_ignore"), ) assert circuit.to_ir( @@ -3639,7 +3690,10 @@ def test_pulse_circuit_conflicting_frame(user_defined_frame): pulse_sequence_user_defined_frame_x = ( PulseSequence() .set_frequency(user_defined_frame_x, 3e9) - .play(user_defined_frame_x, GaussianWaveform(length=1e-3, sigma=0.7, id="gauss_wf")) + .play( + user_defined_frame_x, + GaussianWaveform(length=1e-3, sigma=0.7, id="gauss_wf"), + ) ) pulse_sequence_user_defined_frame = ( @@ -3673,7 +3727,10 @@ def test_parametrized_pulse_circuit(user_defined_frame): pulse_sequence = ( PulseSequence() .set_frequency(user_defined_frame, frequency_parameter) - .play(user_defined_frame, GaussianWaveform(length=length, sigma=0.7, id="gauss_wf")) + .play( + user_defined_frame, + GaussianWaveform(length=length, sigma=0.7, id="gauss_wf"), + ) ) circuit = ( @@ -3826,8 +3883,5 @@ def test_barrier_openqasm_export_all_qubits(): def test_barrier_jaqcd_export_fails(): circ = Circuit().h(0).barrier([0, 1]) - with ( - pytest.warns(UserWarning, match="JAQCD"), - pytest.raises(NotImplementedError, match="Barrier is not supported in JAQCD"), - ): + with pytest.raises(NotImplementedError, match="Barrier is not supported in JAQCD"): circ.to_ir(IRType.JAQCD)