diff --git a/CHANGELOG.md b/CHANGELOG.md index 343965f60..82e97333d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v1.81.1 (2024-06-17) + +### Bug Fixes and Other Changes + + * Error when FreeParameters are named QASM types + +## v1.81.0 (2024-06-13) + +### Features + + * Add IQM to get compiled program convenience method + +## v1.80.1 (2024-06-10) + +### Bug Fixes and Other Changes + + * docs: add stack exchange badge to the readme + * Implement `braket.ahs.AnalogHamiltonianSimulation.from_ir()` + ## v1.80.0 (2024-05-22) ### Features diff --git a/README.md b/README.md index 0c935853d..415a18c25 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Build status](https://github.com/amazon-braket/amazon-braket-sdk-python/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/amazon-braket/amazon-braket-sdk-python/actions/workflows/python-package.yml) [![codecov](https://codecov.io/gh/amazon-braket/amazon-braket-sdk-python/branch/main/graph/badge.svg?token=1lsqkZL3Ll)](https://codecov.io/gh/amazon-braket/amazon-braket-sdk-python) [![Documentation Status](https://img.shields.io/readthedocs/amazon-braket-sdk-python.svg?logo=read-the-docs)](https://amazon-braket-sdk-python.readthedocs.io/en/latest/?badge=latest) +[![Stack Overflow](https://img.shields.io/badge/StackExchange-Ask%20questions-blue?logo=stackexchange)](https://quantumcomputing.stackexchange.com/questions/tagged/amazon-braket) The Amazon Braket Python SDK is an open source library that provides a framework that you can use to interact with quantum computing hardware devices through Amazon Braket. diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index ce2f1eb66..50ee98c7c 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.80.1.dev0" +__version__ = "1.81.2.dev0" diff --git a/src/braket/parametric/free_parameter.py b/src/braket/parametric/free_parameter.py index 1f3a69e72..8f437bbca 100644 --- a/src/braket/parametric/free_parameter.py +++ b/src/braket/parametric/free_parameter.py @@ -20,6 +20,69 @@ from braket.parametric.free_parameter_expression import FreeParameterExpression +PREDEFINED_VARIABLE_NAMES = {"b", "q"} + +# The reserved words are picked from below +# https://github.com/openqasm/openqasm/blob/main/source/grammar/qasm3Lexer.g4 +# https://github.com/openqasm/openpulse-python/blob/main/source/grammar/openpulseLexer.g4 +QASM_RESERVED_WORDS = { + "OPENQASM", + "include", + "defcalgrammar", + "def", + "cal", + "defcal", + "gate", + "extern", + "box", + "let", + "break", + "continue", + "if", + "else", + "end", + "return", + "for", + "while", + "in", + "pragma", + "input", + "output", + "const", + "readonly", + "mutable", + "qreg", + "qubit", + "creg", + "bool", + "bit", + "int", + "uint", + "float", + "angle", + "complex", + "array", + "void", + "duration", + "stretch", + "gphase", + "inv", + "pow", + "ctrl", + "negctrl", + "dim", + "durationof", + "delay", + "reset", + "measure", + "barrier", + "true", + "false", + "waveform", + "port", + "frame", +} + class FreeParameter(FreeParameterExpression): """Class 'FreeParameter' @@ -94,6 +157,16 @@ def _set_name(self, name: str) -> None: raise TypeError("FreeParameter names must be strings") if not name[0].isalpha() and name[0] != "_": raise ValueError("FreeParameter names must start with a letter or an underscore") + if name in PREDEFINED_VARIABLE_NAMES: + raise ValueError( + f"FreeParameter names must not be one of the Braket reserved variable names: " + f"{PREDEFINED_VARIABLE_NAMES}." + ) + if name in QASM_RESERVED_WORDS: + raise ValueError( + f"FreeParameter names must not be one of the OpenQASM or OpenPulse keywords: " + f"{QASM_RESERVED_WORDS}." + ) self._name = Symbol(name) def to_dict(self) -> dict: diff --git a/src/braket/tasks/gate_model_quantum_task_result.py b/src/braket/tasks/gate_model_quantum_task_result.py index 81f90ae7b..e8267032f 100644 --- a/src/braket/tasks/gate_model_quantum_task_result.py +++ b/src/braket/tasks/gate_model_quantum_task_result.py @@ -145,6 +145,8 @@ def get_compiled_circuit(self) -> Optional[str]: return metadata.rigettiMetadata.compiledProgram elif metadata.oqcMetadata: return metadata.oqcMetadata.compiledProgram + elif metadata.iqmMetadata: + return metadata.iqmMetadata.compiledProgram else: return None 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 aec875568..b10891830 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -741,19 +741,19 @@ def test_noise_multi_probabilities(): def test_noise_multi_probabilities_with_parameter(): a = FreeParameter("a") - b = FreeParameter("b") c = FreeParameter("c") - circ = Circuit().h(0).x(1).pauli_channel(1, a, b, c) + d = FreeParameter("d") + circ = Circuit().h(0).x(1).pauli_channel(1, a, c, d) expected = ( "T : | 0 |", " ", "q0 : -H-----------", " ", - "q1 : -X-PC(a,b,c)-", + "q1 : -X-PC(a,c,d)-", "", "T : | 0 |", "", - "Unassigned parameters: [a, b, c].", + "Unassigned parameters: [a, c, d].", ) _assert_correct_diagram(circ, expected) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 0b9ce52d7..285b530c1 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -1069,8 +1069,8 @@ def test_bind_values_pulse_gate(): frame = Frame("user_frame", Port("device_port_x", 1e-9), 1e9) gate = Gate.PulseGate( PulseSequence() - .set_frequency(frame, FreeParameter("a") + FreeParameter("b")) - .delay(frame, FreeParameter("c")), + .set_frequency(frame, FreeParameter("a") + FreeParameter("c")) + .delay(frame, FreeParameter("d")), qubit_count, ) @@ -1084,8 +1084,8 @@ def to_ir(pulse_gate): assert a_bound_ir == "\n".join( [ "cal {", - " set_frequency(user_frame, 3.0 + b);", - " delay[c * 1s] user_frame;", + " set_frequency(user_frame, 3.0 + c);", + " delay[d * 1s] user_frame;", "}", ] ) diff --git a/test/unit_tests/braket/circuits/test_noises.py b/test/unit_tests/braket/circuits/test_noises.py index 5fffba43f..3b7f08dd8 100644 --- a/test/unit_tests/braket/circuits/test_noises.py +++ b/test/unit_tests/braket/circuits/test_noises.py @@ -483,10 +483,10 @@ def test_parameter_binding(parameterized_noise, params, expected_noise): def test_parameterized_noise(): - noise = Noise.PauliChannel(FreeParameter("a"), 0.2, FreeParameter("b")) + noise = Noise.PauliChannel(FreeParameter("a"), 0.2, FreeParameter("d")) assert noise.probX == FreeParameter("a") assert noise.probY == 0.2 - assert noise.probZ == FreeParameter("b") + assert noise.probZ == FreeParameter("d") # Additional Unitary noise tests 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 268c53a96..efc87ee24 100644 --- a/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py @@ -853,20 +853,20 @@ def test_noise_multi_probabilities(): def test_noise_multi_probabilities_with_parameter(): a = FreeParameter("a") - b = FreeParameter("b") c = FreeParameter("c") - circ = Circuit().h(0).x(1).pauli_channel(1, a, b, c) + d = FreeParameter("d") + circ = Circuit().h(0).x(1).pauli_channel(1, a, c, d) expected = ( "T : │ 0 │", " ┌───┐ ", "q0 : ─┤ H ├───────────────", " └───┘ ", " ┌───┐ ┌───────────┐ ", - "q1 : ─┤ X ├─┤ PC(a,b,c) ├─", + "q1 : ─┤ X ├─┤ PC(a,c,d) ├─", " └───┘ └───────────┘ ", "T : │ 0 │", "", - "Unassigned parameters: [a, b, c].", + "Unassigned parameters: [a, c, d].", ) _assert_correct_diagram(circ, expected) diff --git a/test/unit_tests/braket/parametric/test_free_parameter.py b/test/unit_tests/braket/parametric/test_free_parameter.py index b94c60a97..0b42bfe5a 100644 --- a/test/unit_tests/braket/parametric/test_free_parameter.py +++ b/test/unit_tests/braket/parametric/test_free_parameter.py @@ -67,3 +67,21 @@ def test_sub_successful(free_parameter): def test_sub_wrong_param(free_parameter): assert free_parameter.subs({"alpha": 1}) == FreeParameter("theta") + + +@pytest.mark.parametrize("param_name", ["for", "qubit"]) +def test_error_raised_when_reserved_keyword_used(param_name): + with pytest.raises( + ValueError, + match="FreeParameter names must not be one of the OpenQASM or OpenPulse keywords: ", + ): + FreeParameter(param_name) + + +@pytest.mark.parametrize("param_name", ["b", "q"]) +def test_error_raised_when_predefined_variable_used(param_name): + with pytest.raises( + ValueError, + match="FreeParameter names must not be one of the Braket reserved variable names: ", + ): + FreeParameter(param_name) diff --git a/test/unit_tests/braket/parametric/test_free_parameter_expression.py b/test/unit_tests/braket/parametric/test_free_parameter_expression.py index 1bf6818bf..51402482f 100644 --- a/test/unit_tests/braket/parametric/test_free_parameter_expression.py +++ b/test/unit_tests/braket/parametric/test_free_parameter_expression.py @@ -165,7 +165,7 @@ def test_sub_return_expression(): @pytest.mark.parametrize( "param, kwargs, expected_value, expected_type", [ - (FreeParameter("a") + 2 * FreeParameter("b"), {"a": 0.1, "b": 0.3}, 0.7, float), + (FreeParameter("a") + 2 * FreeParameter("d"), {"a": 0.1, "d": 0.3}, 0.7, float), (FreeParameter("x"), {"y": 1}, FreeParameter("x"), FreeParameter), (FreeParameter("y"), {"y": -0.1}, -0.1, float), (2 * FreeParameter("i"), {"i": 1}, 2.0, float), diff --git a/test/unit_tests/braket/pulse/test_pulse_sequence.py b/test/unit_tests/braket/pulse/test_pulse_sequence.py index da8e0a8a3..9b6092db1 100644 --- a/test/unit_tests/braket/pulse/test_pulse_sequence.py +++ b/test/unit_tests/braket/pulse/test_pulse_sequence.py @@ -81,7 +81,7 @@ def test_pulse_sequence_with_user_defined_frame(user_defined_frame): def test_pulse_sequence_make_bound_pulse_sequence(predefined_frame_1, predefined_frame_2): - param = FreeParameter("a") + 2 * FreeParameter("b") + param = FreeParameter("a") + 2 * FreeParameter("c") pulse_sequence = ( PulseSequence() .set_frequency(predefined_frame_1, param) @@ -135,14 +135,14 @@ def test_pulse_sequence_make_bound_pulse_sequence(predefined_frame_1, predefined " sigma_dg * 1s, 0.2, 1, false);", " waveform constant_wf = constant(length_c * 1s, 2.0 + 0.3im);", " waveform arb_wf = {1.0 + 0.4im, 0, 0.3, 0.1 + 0.2im};", - " set_frequency(predefined_frame_1, a + 2.0 * b);", - " shift_frequency(predefined_frame_1, a + 2.0 * b);", - " set_phase(predefined_frame_1, a + 2.0 * b);", - " shift_phase(predefined_frame_1, -1.0 * a + -2.0 * b);", - " set_scale(predefined_frame_1, a + 2.0 * b);", + " set_frequency(predefined_frame_1, a + 2.0 * c);", + " shift_frequency(predefined_frame_1, a + 2.0 * c);", + " set_phase(predefined_frame_1, a + 2.0 * c);", + " shift_phase(predefined_frame_1, -1.0 * a + -2.0 * c);", + " set_scale(predefined_frame_1, a + 2.0 * c);", " psb[0] = capture_v0(predefined_frame_1);", - " delay[(a + 2.0 * b) * 1s] predefined_frame_1, predefined_frame_2;", - " delay[(a + 2.0 * b) * 1s] predefined_frame_1;", + " delay[(a + 2.0 * c) * 1s] predefined_frame_1, predefined_frame_2;", + " delay[(a + 2.0 * c) * 1s] predefined_frame_1;", " delay[1.0ms] predefined_frame_1;", " barrier predefined_frame_1, predefined_frame_2;", " play(predefined_frame_1, gauss_wf);", @@ -156,7 +156,7 @@ def test_pulse_sequence_make_bound_pulse_sequence(predefined_frame_1, predefined assert pulse_sequence.to_ir() == expected_str_unbound assert pulse_sequence.parameters == { FreeParameter("a"), - FreeParameter("b"), + FreeParameter("c"), FreeParameter("length_g"), FreeParameter("length_dg"), FreeParameter("sigma_g"), @@ -164,9 +164,9 @@ def test_pulse_sequence_make_bound_pulse_sequence(predefined_frame_1, predefined FreeParameter("length_c"), } b_bound = pulse_sequence.make_bound_pulse_sequence( - {"b": 2, "length_g": 1e-3, "length_dg": 3e-3, "sigma_dg": 0.4, "length_c": 4e-3} + {"c": 2, "length_g": 1e-3, "length_dg": 3e-3, "sigma_dg": 0.4, "length_c": 4e-3} ) - b_bound_call = pulse_sequence(b=2, length_g=1e-3, length_dg=3e-3, sigma_dg=0.4, length_c=4e-3) + b_bound_call = pulse_sequence(c=2, length_g=1e-3, length_dg=3e-3, sigma_dg=0.4, length_c=4e-3) expected_str_b_bound = "\n".join( [ "OPENQASM 3.0;", 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 5447bd8b2..99ab7e345 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 @@ -26,6 +26,7 @@ ResultTypeValue, TaskMetadata, ) +from braket.task_result.iqm_metadata_v1 import IqmMetadata from braket.task_result.oqc_metadata_v1 import OqcMetadata from braket.task_result.rigetti_metadata_v1 import RigettiMetadata from braket.tasks import GateModelQuantumTaskResult @@ -103,6 +104,22 @@ def additional_metadata_rigetti(quil_program): return AdditionalMetadata(action=program, rigettiMetadata=rigetti_metadata) +@pytest.fixture +def additional_metadata_iqm(): + source = """ + OPENQASM 3.0; + bit[2] b; + h $0; + cnot $0, $7; + b[0] = measure $0; + b[1] = measure $7; + """ + program = openqasm.Program(source=source) + iqm_metadata = IqmMetadata(compiledProgram=source) + + return AdditionalMetadata(action=program, iqmMetadata=iqm_metadata) + + @pytest.fixture def qasm2_program(): return """ @@ -160,6 +177,13 @@ def result_oqc(result_obj_1, additional_metadata_oqc): return result +@pytest.fixture +def result_iqm(result_obj_1, additional_metadata_iqm): + result = GateModelQuantumTaskResult.from_object(result_obj_1) + result.additional_metadata = additional_metadata_iqm + return result + + @pytest.fixture def result_str_1(result_obj_1): return result_obj_1.json() @@ -323,6 +347,11 @@ def test_get_compiled_circuit_oqc(result_oqc, qasm2_program): assert result_oqc.get_compiled_circuit() == qasm2_program +def test_get_compiled_circuit_iqm(result_iqm): + """Test get_compiled_circuit method.""" + assert result_iqm.get_compiled_circuit() == result_iqm.additional_metadata.action.source + + def test_get_compiled_circuit_no_qhp_metadata(result_obj_1): """Test get_compiled_circuit method.""" result = GateModelQuantumTaskResult.from_object(result_obj_1)