From 2bb1e743da965f6cc459b52d3cf580a53357741e Mon Sep 17 00:00:00 2001 From: Milan <30416311+krneta@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:07:39 -0800 Subject: [PATCH 01/21] update: adding a test to check for circular imports (#812) --- test/unit_tests/braket/test_imports.py | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/unit_tests/braket/test_imports.py diff --git a/test/unit_tests/braket/test_imports.py b/test/unit_tests/braket/test_imports.py new file mode 100644 index 000000000..bd545d943 --- /dev/null +++ b/test/unit_tests/braket/test_imports.py @@ -0,0 +1,48 @@ +import importlib +import multiprocessing +import os +import pathlib + +import pytest + + +def test_for_import_cycles(): + # Note, because of all the multiprocessing in this test, when running 'tox', the process + # threads may be made to wait as other tests are running in parallel, making it seems like + # this test is much slower than it actually is. However, splitting the test into a + # parameterized version wasn't able to correctly detect some circular imports when running tox. + modules = get_modules_to_test() + processes = [] + multiprocessing.set_start_method("spawn") + for module in modules: + # We create a separate process to make sure the imports do not interfere with each-other. + process = multiprocessing.Process(target=import_module, args=(module,)) + processes.append(process) + process.start() + + for index, process in enumerate(processes): + process.join() + if process.exitcode != 0: + pytest.fail( + f"Unable to import '{modules[index]}'." + " If all other tests are passing, check for cyclical dependencies." + ) + + +def get_modules_to_test(): + curr_path = pathlib.Path(__file__).resolve() + while "test" in str(curr_path): + curr_path = curr_path.parent + curr_path = curr_path.joinpath("src") + curr_path_len = len(str(curr_path)) + len(os.sep) + modules = [] + for dir_, temp, files in os.walk(curr_path): + # Rather than testing every single python file we just test modules, for now. + if "__init__.py" in files: + braket_module = dir_[curr_path_len:].replace(os.sep, ".") + modules.append(braket_module) + return modules + + +def import_module(module): + importlib.import_module(module) From dedfb7206f301913d343520b9b2a6b4c7db62562 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 6 Dec 2023 11:09:52 -0800 Subject: [PATCH 02/21] infra: no unfrozen prefix when variable is not present (#810) --- .github/workflows/code-freeze.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/code-freeze.yml b/.github/workflows/code-freeze.yml index 5aff0abc7..d19f5b48d 100644 --- a/.github/workflows/code-freeze.yml +++ b/.github/workflows/code-freeze.yml @@ -32,8 +32,18 @@ jobs: echo $BRANCH_NAME echo $PR_TITLE - if [[ "$BRANCH_NAME" != $UNFROZEN_PREFIX* ]] && - [[ "$PR_TITLE" != fix:* && "$PR_TITLE" != *"[critical]"* ]]; then - echo "Error: You can only merge from branches that start with '$UNFROZEN_PREFIX', or PRs titled with 'fix: ' and containing '[critical]'." - exit 1 + # if it's not a critical fix + if ! [[ "$PR_TITLE" == fix\(critical\):* ]]; then + # and there's an unfrozen prefix + if ! [[ -z $UNFROZEN_PREFIX ]]; then + # check if the branch matches unfrozen prefix + if [[ "$BRANCH_NAME" != $UNFROZEN_PREFIX* ]]; then + echo "Error: You can only merge from branches that start with '$UNFROZEN_PREFIX', or PRs titled with prefix 'fix(critical): '." + exit 1 + fi + # repo is fully frozen + else + echo "Error: You can only merge PRs titled with prefix 'fix(critical): '." + exit 1 + fi fi From 608a8dc3f72da42ee8913eb38fb9078e2121e50a Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 6 Dec 2023 12:42:20 -0800 Subject: [PATCH 03/21] feat: add str, repr and getitem to BasisState (#808) --- src/braket/circuits/basis_state.py | 12 ++++ .../braket/circuits/test_basis_state.py | 58 ++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 814444e75..b6ce11bc8 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -33,6 +33,18 @@ def __iter__(self): def __eq__(self, other): return self.state == other.state + def __bool__(self): + return any(self.state) + + def __str__(self): + return self.as_string + + def __repr__(self): + return f'BasisState("{self.as_string}")' + + def __getitem__(self, item): + return BasisState(self.state[item]) + BasisStateInput = Union[int, list[int], str, BasisState] diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 023494fae..166e7c8fc 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -51,6 +51,58 @@ ), ) def test_as_props(basis_state_input, size, as_tuple, as_int, as_string): - assert BasisState(basis_state_input, size).as_tuple == as_tuple - assert BasisState(basis_state_input, size).as_int == as_int - assert BasisState(basis_state_input, size).as_string == as_string + basis_state = BasisState(basis_state_input, size) + assert basis_state.as_tuple == as_tuple + assert basis_state.as_int == as_int + assert basis_state.as_string == as_string == str(basis_state) + assert repr(basis_state) == f'BasisState("{as_string}")' + + +@pytest.mark.parametrize( + "basis_state_input, index, substate_input", + ( + ( + "1001", + slice(None), + "1001", + ), + ( + "1001", + 3, + "1", + ), + ( + "1010", + slice(None, None, 2), + "11", + ), + ( + "1010", + slice(1, None, 2), + "00", + ), + ( + "1010", + slice(None, -2), + "10", + ), + ( + "1010", + -1, + "0", + ), + ), +) +def test_indexing(basis_state_input, index, substate_input): + assert BasisState(basis_state_input)[index] == BasisState(substate_input) + + +def test_bool(): + assert all( + [ + BasisState("100"), + BasisState("111"), + BasisState("1"), + ] + ) + assert not BasisState("0") From 788b8b68a2835ee7ffb1d415c4dda0f4cf29c6bc Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 7 Dec 2023 16:17:28 +0000 Subject: [PATCH 04/21] prepare release v1.64.0 --- CHANGELOG.md | 10 ++++++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f3514294..c8c50a7da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## v1.64.0 (2023-12-07) + +### Features + + * add str, repr and getitem to BasisState + +### Bug Fixes and Other Changes + + * update: adding a test to check for circular imports + ## v1.63.0 (2023-12-05) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 70ca418cd..38e0376ee 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.63.1.dev0" +__version__ = "1.64.0" From 372cd53be84bbd27e1360b2ba15a5823404cd2da Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 7 Dec 2023 16:17:28 +0000 Subject: [PATCH 05/21] update development version to v1.64.1.dev0 --- src/braket/_sdk/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 38e0376ee..156b961b8 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.64.0" +__version__ = "1.64.1.dev0" From d47c6fcfa16832fedc4fc8a95b7f3a6846e1a7fe Mon Sep 17 00:00:00 2001 From: Abe Coull <85974725+math411@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:28:29 -0800 Subject: [PATCH 06/21] infra: allow tox to use logical processors (#824) Co-authored-by: Abe Coull Co-authored-by: Cody Wang --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 94f28ec7d..0a6ad7c49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ test=pytest xfail_strict = true # https://pytest-xdist.readthedocs.io/en/latest/known-limitations.html addopts = - --verbose -n auto --durations=0 --durations-min=1 + --verbose -n logical --durations=0 --durations-min=1 testpaths = test/unit_tests [isort] diff --git a/setup.py b/setup.py index f15777485..86ec939e3 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ "pytest", "pytest-cov", "pytest-rerunfailures", - "pytest-xdist", + "pytest-xdist[psutil]", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-apidoc", From bc126bc5062a26a4c9df04a3c2bd022866769e51 Mon Sep 17 00:00:00 2001 From: Abe Coull <85974725+math411@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:47:39 -0800 Subject: [PATCH 07/21] infra: ignore rsyncdir warning raised by rsync testing in pytest-cov (#817) --- setup.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.cfg b/setup.cfg index 0a6ad7c49..3bad103a3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,11 @@ xfail_strict = true addopts = --verbose -n logical --durations=0 --durations-min=1 testpaths = test/unit_tests +filterwarnings= + # Issue #557 in `pytest-cov` (currently v4.x) has not moved for a while now, + # but once a resolution has been adopted we can drop this "ignore". + # Ref: https://github.com/pytest-dev/pytest-cov/issues/557 + ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning [isort] line_length = 100 From 37bad90832b07a7ba81fbe88d1a2ae0213562847 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:18:31 -0700 Subject: [PATCH 08/21] infra: bump actions/setup-python from 4.7.1 to 5.0.0 (#833) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.1 to 5.0.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236...0a5c61591373683505ea898e09a3ea4f39ef2b9c) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-format.yml | 2 +- .github/workflows/dependent-tests.yml | 2 +- .github/workflows/publish-to-pypi.yml | 2 +- .github/workflows/python-package.yml | 2 +- .github/workflows/twine-check.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index dbe9599c1..1795135e7 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Python - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: '3.9' - name: Install dependencies diff --git a/.github/workflows/dependent-tests.yml b/.github/workflows/dependent-tests.yml index cfd395241..aca79d599 100644 --- a/.github/workflows/dependent-tests.yml +++ b/.github/workflows/dependent-tests.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index e4d4e68c5..b12cde750 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Python - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: '3.x' - name: Install wheel diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0c02a7561..56ba81bf4 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/twine-check.yml b/.github/workflows/twine-check.yml index f8e01fd72..2c30b8ec0 100644 --- a/.github/workflows/twine-check.yml +++ b/.github/workflows/twine-check.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Python - uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: '3.x' - name: Install wheel From 7787d27123845eab2e5416fa1dd975ac8409a406 Mon Sep 17 00:00:00 2001 From: Abe Coull <85974725+math411@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:14:00 -0800 Subject: [PATCH 09/21] infra: Install only tox for python-package workflow (#791) --- .github/workflows/python-package.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 56ba81bf4..42dda2020 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -31,8 +31,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - pip install --upgrade pip - pip install -e .[test] + pip install tox - name: Run unit tests run: | tox -e unit-tests From 12f1387e0c715b398ed102a996fd436b77f297a5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula <99367153+jcjaskula-aws@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:00:57 -0500 Subject: [PATCH 10/21] fix: make filter more convenient (#718) * make filter more convenient * changes according to feedback * remove Union type hint --------- Co-authored-by: Abe Coull <85974725+math411@users.noreply.github.com> Co-authored-by: Cody Wang --- src/braket/circuits/gate_calibrations.py | 25 +++++++++++-------- .../braket/circuits/test_gate_calibration.py | 19 +++++++++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/braket/circuits/gate_calibrations.py b/src/braket/circuits/gate_calibrations.py index 6cbdd97d1..57013df4a 100644 --- a/src/braket/circuits/gate_calibrations.py +++ b/src/braket/circuits/gate_calibrations.py @@ -14,7 +14,7 @@ from __future__ import annotations from copy import deepcopy -from typing import Any, Optional +from typing import Any from braket.circuits.gate import Gate from braket.circuits.serialization import ( @@ -91,35 +91,40 @@ def __len__(self): return len(self._pulse_sequences) def filter( - self, gates: Optional[list[Gate]] = None, qubits: Optional[QubitSet] = None - ) -> Optional[GateCalibrations]: + self, + gates: list[Gate] | None = None, + qubits: QubitSet | list[QubitSet] | None = None, + ) -> GateCalibrations: """ Filters the data based on optional lists of gates and QubitSets. Args: - gates (Optional[list[Gate]]): An optional list of gates to filter on. - qubits (Optional[QubitSet]): An optional `QubitSet` to filter on. + gates (list[Gate] | None): An optional list of gates to filter on. + qubits (QubitSet | list[QubitSet] | None): An optional `QubitSet` or + list of `QubitSet` to filter on. Returns: - Optional[GateCalibrations]: A filtered GateCalibrations object. Otherwise, returns - none if no matches are found. + GateCalibrations: A filtered GateCalibrations object. """ # noqa: E501 keys = self.pulse_sequences.keys() + if isinstance(qubits, QubitSet): + qubits = [qubits] filtered_calibration_keys = [ tup for tup in keys - if (gates is None or tup[0] in gates) and (qubits is None or qubits.issubset(tup[1])) + if (gates is None or tup[0] in gates) + and (qubits is None or any(qset.issubset(tup[1]) for qset in qubits)) ] return GateCalibrations( {k: v for (k, v) in self.pulse_sequences.items() if k in filtered_calibration_keys}, ) - def to_ir(self, calibration_key: Optional[tuple[Gate, QubitSet]] = None) -> str: + def to_ir(self, calibration_key: tuple[Gate, QubitSet] | None = None) -> str: """ Returns the defcal representation for the `GateCalibrations` object. Args: - calibration_key (Optional[tuple[Gate, QubitSet]]): An optional key to get a specific defcal. + calibration_key (tuple[Gate, QubitSet] | None): An optional key to get a specific defcal. Default: None Returns: diff --git a/test/unit_tests/braket/circuits/test_gate_calibration.py b/test/unit_tests/braket/circuits/test_gate_calibration.py index 31c2384db..c95ce74a3 100644 --- a/test/unit_tests/braket/circuits/test_gate_calibration.py +++ b/test/unit_tests/braket/circuits/test_gate_calibration.py @@ -57,19 +57,30 @@ def test_gc_copy(pulse_sequence): def test_filter(pulse_sequence): - calibration_key = (Gate.Z(), QubitSet([0, 1])) - calibration_key_2 = (Gate.H(), QubitSet([0, 1])) + calibration_key = (Gate.Z(), QubitSet([0])) + calibration_key_2 = (Gate.H(), QubitSet([1])) + calibration_key_3 = (Gate.CZ(), QubitSet([0, 1])) calibration = GateCalibrations( - {calibration_key: pulse_sequence, calibration_key_2: pulse_sequence} + { + calibration_key: pulse_sequence, + calibration_key_2: pulse_sequence, + calibration_key_3: pulse_sequence, + } ) expected_calibration_1 = GateCalibrations({calibration_key: pulse_sequence}) expected_calibration_2 = GateCalibrations( - {calibration_key: pulse_sequence, calibration_key_2: pulse_sequence} + {calibration_key: pulse_sequence, calibration_key_3: pulse_sequence} ) expected_calibration_3 = GateCalibrations({calibration_key_2: pulse_sequence}) + expected_calibration_4 = GateCalibrations({}) + expected_calibration_5 = calibration + expected_calibration_6 = GateCalibrations({calibration_key_3: pulse_sequence}) assert expected_calibration_1 == calibration.filter(gates=[Gate.Z()]) assert expected_calibration_2 == calibration.filter(qubits=QubitSet(0)) assert expected_calibration_3 == calibration.filter(gates=[Gate.H()], qubits=QubitSet(1)) + assert expected_calibration_4 == calibration.filter(gates=[Gate.Z()], qubits=QubitSet(1)) + assert expected_calibration_5 == calibration.filter(qubits=[QubitSet(0), QubitSet(1)]) + assert expected_calibration_6 == calibration.filter(qubits=QubitSet([0, 1])) def test_to_ir(pulse_sequence): From c1eb6644aab1dde1165d5d13e8a556eb04374bb5 Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 12 Dec 2023 16:16:20 +0000 Subject: [PATCH 11/21] prepare release v1.64.1 --- CHANGELOG.md | 6 ++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8c50a7da..64c04fee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.64.1 (2023-12-12) + +### Bug Fixes and Other Changes + + * make filter more convenient + ## v1.64.0 (2023-12-07) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 156b961b8..1f45b0ddf 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.64.1.dev0" +__version__ = "1.64.1" From f44ab586d67a86464d3de7be282bcf878776a940 Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 12 Dec 2023 16:16:20 +0000 Subject: [PATCH 12/21] update development version to v1.64.2.dev0 --- src/braket/_sdk/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 1f45b0ddf..d63d59133 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.64.1" +__version__ = "1.64.2.dev0" From 228f0802716bc9cef5d886fd2aab3e6ec100929b Mon Sep 17 00:00:00 2001 From: Milan <30416311+krneta@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:19:06 -0800 Subject: [PATCH 13/21] fix: treating OpenQASM builtin types as constants (#829) --- src/braket/circuits/braket_program_context.py | 7 +++-- .../braket/circuits/test_circuit.py | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/braket_program_context.py b/src/braket/circuits/braket_program_context.py index 9d8c69afe..19e2c986f 100644 --- a/src/braket/circuits/braket_program_context.py +++ b/src/braket/circuits/braket_program_context.py @@ -14,7 +14,7 @@ from typing import Optional, Union import numpy as np -from sympy import Expr +from sympy import Expr, Number from braket.circuits import Circuit, Instruction from braket.circuits.gates import Unitary @@ -147,5 +147,8 @@ def handle_parameter_value( otherwise wraps the symbolic expression as a `FreeParameterExpression`. """ if isinstance(value, Expr): - return FreeParameterExpression(value) + evaluated_value = value.evalf() + if isinstance(evaluated_value, Number): + return evaluated_value + return FreeParameterExpression(evaluated_value) return value diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 5824967b8..71cff0de7 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -1718,6 +1718,36 @@ def foo( inputs={}, ), ), + ( + Circuit().rx(0, np.pi), + OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[1] q;", + "rx(π) q[0];", + "b[0] = measure q[0];", + ] + ), + inputs={}, + ), + ), + ( + Circuit().rx(0, 2 * np.pi), + OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[1] q;", + "rx(τ) q[0];", + "b[0] = measure q[0];", + ] + ), + inputs={}, + ), + ), ], ) def test_from_ir(expected_circuit, ir): From b33191f4204388e33a0af52cae2dafb979aff6af Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 19 Dec 2023 16:18:49 +0000 Subject: [PATCH 14/21] prepare release v1.64.2 --- CHANGELOG.md | 6 ++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64c04fee2..3fc496e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.64.2 (2023-12-19) + +### Bug Fixes and Other Changes + + * treating OpenQASM builtin types as constants + ## v1.64.1 (2023-12-12) ### Bug Fixes and Other Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index d63d59133..d2cdebb9d 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.64.2.dev0" +__version__ = "1.64.2" From d45c9c8429877ec843fa5886672a974189be51ff Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 19 Dec 2023 16:18:49 +0000 Subject: [PATCH 15/21] update development version to v1.64.3.dev0 --- src/braket/_sdk/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index d2cdebb9d..a30db2260 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.64.2" +__version__ = "1.64.3.dev0" From 5d8d04c850459ac54633042ac41a8c0eebc3dc1c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula <99367153+jcjaskula-aws@users.noreply.github.com> Date: Wed, 20 Dec 2023 18:25:24 -0500 Subject: [PATCH 16/21] feat: add U and GPhase gates (#799) * Add U gate * modification according to feedback * fix linters * clean commented code * first version of gphase * handle drawing and control global phase * Adding a global phase attribute * add global phase to circuit unitary * first draft tests to check coverage * add more tests * add test with parametric global phase * add test for neg control qubit printing * clean up * simplify ctrl-gphase transform * feat: add str, repr and getitem to BasisState * add repr coverage * add index * add pop * fix phase target qubit * fix typing * add index and pop tests * fix code coverage * move unitary matrices * use a subindex in MomentKey * print global phase integration * fix docstrings * fix circuits zero total global phase * fix edge cases * fix to_unitary * temporary fix that checks classname * clean up test conditions * change logic according to feedback * update docstring * clean tests * update tests * replace control symbols * use box drawing characters * Revert "use box drawing characters" This reverts commit ccb81fa641fea45c7b3b8b2a597e7c4804e60b25. * Revert "replace control symbols" This reverts commit 4efb8bc53ba1d6bb42ba3df395a71ec324f7ea0d. * simplify all gphase case * change preprare_y_axis function name * create an helper function to compute the global phase * make control_basis_state more explicit * add comment and clean grouping * add test_only_neg_control_qubits * parametrize test_one_gate_with_global_phase * reformat * change to printing with fixed precision * fix docstring --------- Co-authored-by: Aaron Berdy --- src/braket/circuits/ascii_circuit_diagram.py | 148 +++++++++-- src/braket/circuits/braket_program_context.py | 11 +- src/braket/circuits/circuit.py | 13 +- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gates.py | 237 +++++++++++++++++- src/braket/circuits/moments.py | 24 +- src/braket/circuits/translations.py | 2 + .../braket/circuits/test_angled_gate.py | 2 +- .../circuits/test_ascii_circuit_diagram.py | 84 +++++++ .../braket/circuits/test_circuit.py | 46 ++++ test/unit_tests/braket/circuits/test_gates.py | 100 +++++++- 11 files changed, 639 insertions(+), 30 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index daef01793..2c7024574 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -21,8 +21,10 @@ from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction +from braket.circuits.moments import MomentType from braket.circuits.noise import Noise from braket.circuits.result_type import ResultType +from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet @@ -44,23 +46,26 @@ def build_diagram(circuit: cir.Circuit) -> str: if not circuit.instructions: return "" + if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + return f"Global phase: {circuit.global_phase}" + circuit_qubits = circuit.qubits circuit_qubits.sort() - # Y Axis Column - y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) - for qubit in circuit_qubits: - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) + y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars( + circuit, circuit_qubits + ) time_slices = circuit.moments.time_slices() column_strs = [] # Moment columns for time, instructions in time_slices.items(): + global_phase = AsciiCircuitDiagram._compute_moment_global_phase( + global_phase, instructions + ) moment_str = AsciiCircuitDiagram._ascii_diagram_column_set( - str(time), circuit_qubits, instructions + str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) @@ -71,7 +76,7 @@ def build_diagram(circuit: cir.Circuit) -> str: if target_result_types: column_strs.append( AsciiCircuitDiagram._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types + "Result Types", circuit_qubits, target_result_types, global_phase ) ) @@ -84,6 +89,9 @@ def build_diagram(circuit: cir.Circuit) -> str: # Time on top and bottom lines.append(lines[0]) + if global_phase: + lines.append(f"\nGlobal phase: {global_phase}") + # Additional result types line on bottom if additional_result_types: lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") @@ -97,6 +105,49 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) + @staticmethod + def _prepare_diagram_vars( + circuit: cir.Circuit, circuit_qubits: QubitSet + ) -> tuple[str, float | None]: + # Y Axis Column + y_axis_width = len(str(int(max(circuit_qubits)))) + y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) + + global_phase = None + if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width) + global_phase = 0 + + for qubit in circuit_qubits: + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) + + return y_axis_str, global_phase + + @staticmethod + def _compute_moment_global_phase( + global_phase: float | None, items: list[Instruction] + ) -> float | None: + """ + Compute the integrated phase at a certain moment. + + Args: + global_phase (float | None): The integrated phase up to the computed moment + items (list[Instruction]): list of instructions + + Returns: + float | None: The updated integrated phase. + """ + moment_phase = 0 + for item in items: + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + moment_phase += item.operator.angle + return global_phase + moment_phase if global_phase is not None else None + @staticmethod def _ascii_group_items( circuit_qubits: QubitSet, @@ -120,7 +171,15 @@ def _ascii_group_items( ): continue - if (isinstance(item, ResultType) and not item.target) or ( + # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range + # to an empty list and we just add it to the first group below. + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + qubit_range = QubitSet() + elif (isinstance(item, ResultType) and not item.target) or ( isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) ): qubit_range = circuit_qubits @@ -175,7 +234,10 @@ def _categorize_result_types( @staticmethod def _ascii_diagram_column_set( - col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]] + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None, ) -> str: """ Return a set of columns in the ASCII string diagram of the circuit for a list of items. @@ -184,6 +246,7 @@ def _ascii_diagram_column_set( col_title (str): title of column set circuit_qubits (QubitSet): qubits in circuit items (list[Union[Instruction, ResultType]]): list of instructions or result types + global_phase (float | None): the integrated global phase up to this set Returns: str: An ASCII string diagram for the column set. @@ -193,7 +256,7 @@ def _ascii_diagram_column_set( groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items) column_strs = [ - AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1]) + AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) for grouping in groupings ] @@ -220,7 +283,9 @@ def _ascii_diagram_column_set( @staticmethod def _ascii_diagram_column( - circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]] + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None = None, ) -> str: """ Return a column in the ASCII string diagram of the circuit for a given list of items. @@ -228,9 +293,10 @@ def _ascii_diagram_column( Args: circuit_qubits (QubitSet): qubits in circuit items (list[Union[Instruction, ResultType]]): list of instructions or result types + global_phase (float | None): the integrated global phase up to this column Returns: - str: An ASCII string diagram for the specified moment in time for a column. + str: an ASCII string diagram for the specified moment in time for a column. """ symbols = {qubit: "-" for qubit in circuit_qubits} margins = {qubit: " " for qubit in circuit_qubits} @@ -252,12 +318,26 @@ def _ascii_diagram_column( num_after = len(circuit_qubits) - 1 after = ["|"] * (num_after - 1) + ([marker] if num_after else []) ascii_symbols = [ascii_symbol] + after + elif ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + target_qubits = circuit_qubits + control_qubits = QubitSet() + target_and_control = QubitSet() + qubits = circuit_qubits + ascii_symbols = "-" * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) else: target_qubits = item.target control_qubits = getattr(item, "control", QubitSet()) + map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( + item, control_qubits + ) + target_and_control = target_qubits.union(control_qubits) qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) @@ -288,20 +368,54 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: - symbols[qubit] = "C" + symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" else: symbols[qubit] = "|" # Set the margin to be a connector if not on the first qubit - if qubit != min(target_and_control): + if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - symbols_width = max([len(symbol) for symbol in symbols.values()]) + output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) + return output + @staticmethod + def _create_output( + symbols: dict[Qubit, str], + margins: dict[Qubit, str], + qubits: QubitSet, + global_phase: float | None, + ) -> str: + symbols_width = max([len(symbol) for symbol in symbols.values()]) output = "" - for qubit in circuit_qubits: + + if global_phase is not None: + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) + ) + symbols_width = max([symbols_width, len(global_phase_str)]) + output += "{0:{fill}{align}{width}}|\n".format( + global_phase_str, + fill=" ", + align="^", + width=symbols_width, + ) + + for qubit in qubits: output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( symbols[qubit], fill="-", align="<", width=symbols_width + 1 ) return output + + @staticmethod + def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int): + control_state = getattr(item, "control_state", None) + if control_state is not None: + map_control_qubit_states = { + qubit: state for qubit, state in zip(control_qubits, control_state) + } + else: + map_control_qubit_states = {qubit: 1 for qubit in control_qubits} + + return map_control_qubit_states diff --git a/src/braket/circuits/braket_program_context.py b/src/braket/circuits/braket_program_context.py index 19e2c986f..863513565 100644 --- a/src/braket/circuits/braket_program_context.py +++ b/src/braket/circuits/braket_program_context.py @@ -56,8 +56,15 @@ def is_builtin_gate(self, name: str) -> bool: user_defined_gate = self.is_user_defined_gate(name) return name in BRAKET_GATES and not user_defined_gate - def add_phase_instruction(self, target: tuple[int], phase_value: int) -> None: - raise NotImplementedError + def add_phase_instruction(self, target: tuple[int], phase_value: float) -> None: + """Add a global phase to the circuit. + + Args: + target (tuple[int]): Unused + phase_value (float): The phase value to be applied + """ + instruction = Instruction(BRAKET_GATES["gphase"](phase_value)) + self._circuit.add_instruction(instruction) def add_gate_instruction( self, gate_name: str, target: tuple[int], *params, ctrl_modifiers: list[int], power: float diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 76e986087..aeae8585d 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -26,7 +26,7 @@ from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction -from braket.circuits.moments import Moments +from braket.circuits.moments import Moments, MomentType from braket.circuits.noise import Noise from braket.circuits.noise_helpers import ( apply_noise_to_gates, @@ -156,6 +156,17 @@ def depth(self) -> int: """int: Get the circuit depth.""" return self._moments.depth + @property + def global_phase(self) -> float: + """float: Get the global phase of the circuit.""" + return sum( + [ + instr.operator.angle + for moment, instr in self._moments.items() + if moment.moment_type == MomentType.GLOBAL_PHASE + ] + ) + @property def instructions(self) -> list[Instruction]: """Iterable[Instruction]: Get an `iterable` of instructions in the circuit.""" diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 3c59409e5..2183a2329 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -198,7 +198,7 @@ def _to_openqasm( return ( f"{inv_prefix}{power_prefix}{control_prefix}" - f"{self._qasm_name}{param_string} {', '.join(qubits)};" + f"{self._qasm_name}{param_string}{','.join([f' {qubit}' for qubit in qubits])};" ) @property diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 9a4214c37..e955c4bb0 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -30,7 +30,7 @@ angled_ascii_characters, get_angle, ) -from braket.circuits.basis_state import BasisStateInput +from braket.circuits.basis_state import BasisState, BasisStateInput from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate @@ -215,6 +215,118 @@ def i( Gate.register_gate(I) +class GPhase(AngledGate): + r"""Global phase gate. + + Unitary matrix: + + .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_1. + + Args: + angle (Union[FreeParameterExpression, float]): angle in radians. + """ + + def __init__(self, angle: Union[FreeParameterExpression, float]): + # Avoid parent constructor because _qubit_count must be zero + self._qubit_count = self.fixed_qubit_count() + self._ascii_symbols = [] + + if angle is None: + raise ValueError("angle must not be None") + if isinstance(angle, FreeParameterExpression): + self._parameters = [angle] + else: + self._parameters = [float(angle)] # explicit casting in case angle is e.g. np.float32 + + @property + def _qasm_name(self) -> str: + return "gphase" + + def adjoint(self) -> list[Gate]: + return [GPhase(-self.angle)] + + def to_matrix(self) -> np.ndarray: + return np.exp(1j * self.angle) * np.eye(1, dtype=complex) + + def bind_values(self, **kwargs) -> AngledGate: + return get_angle(self, **kwargs) + + @staticmethod + def fixed_qubit_count() -> int: + return 0 + + @staticmethod + @circuit.subroutine(register=True) + def gphase( + angle: Union[FreeParameterExpression, float], + *, + control: Optional[QubitSetInput] = None, + control_state: Optional[BasisStateInput] = None, + power: float = 1, + ) -> Instruction | Iterable[Instruction]: + r"""Global phase gate. + + If the gate is applied with control/negative control modifiers, it is translated in an + equivalent gate using the following definition: `phaseshift(λ) = ctrl @ gphase(λ)`. + The rightmost control qubit is used for the translation. If the polarity of the rightmost + control modifier is negative, the following identity is used: + `negctrl @ gphase(λ) q = x q; ctrl @ gphase(λ) q; x q`. + + Unitary matrix: + + .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_1. + + Args: + angle (Union[FreeParameterExpression, float]): Phase in radians. + control (Optional[QubitSetInput]): Control qubit(s). Default None. + control_state (Optional[BasisStateInput]): Quantum state on which to control the + operation. Must be a binary sequence of same length as number of qubits in + `control`. Will be ignored if `control` is not present. May be represented as a + string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent + controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being + in the \\|1⟩ state. Default "1" * len(control). + power (float): Integer or fractional power to raise the gate to. Negative + powers will be split into an inverse, accompanied by the positive power. + Default 1. + + Returns: + Instruction | Iterable[Instruction]: GPhase instruction. + + Examples: + >>> circ = Circuit().gphase(0.45) + """ + if control is not None: + control_qubits = QubitSet(control) + + control_state = ( + control_state if control_state is not None else (1,) * len(control_qubits) + ) + control_basis_state = BasisState(control_state, len(control_qubits)) + + phaseshift_target = control_qubits[-1] + phaseshift_instruction = PhaseShift.phaseshift( + phaseshift_target, + angle, + control=control_qubits[:-1], + control_state=control_basis_state[:-1], + power=power, + ) + return ( + phaseshift_instruction + if control_basis_state[-1] + else [ + X.x(phaseshift_target), + phaseshift_instruction, + X.x(phaseshift_target), + ] + ) + + return Instruction(GPhase(angle), power=power) + + +Gate.register_gate(GPhase) + + class X(Gate): r"""Pauli-X gate. @@ -1287,6 +1399,129 @@ def phaseshift( Gate.register_gate(PhaseShift) +class U(TripleAngledGate): + r"""Generalized single-qubit rotation gate. + + Unitary matrix: + + .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} + \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} + \end{bmatrix}. + + Args: + angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. + angle_2 (Union[FreeParameterExpression, float]): phi angle in radians. + angle_3 (Union[FreeParameterExpression, float]): lambda angle in radians. + """ + + def __init__( + self, + angle_1: Union[FreeParameterExpression, float], + angle_2: Union[FreeParameterExpression, float], + angle_3: Union[FreeParameterExpression, float], + ): + super().__init__( + angle_1=angle_1, + angle_2=angle_2, + angle_3=angle_3, + qubit_count=None, + ascii_symbols=[_multi_angled_ascii_characters("U", angle_1, angle_2, angle_3)], + ) + + @property + def _qasm_name(self) -> str: + return "U" + + def to_matrix(self) -> np.ndarray: + r"""Returns a matrix representation of this gate. + Returns: + ndarray: The matrix representation of this gate. + """ + _theta = self.angle_1 + _phi = self.angle_2 + _lambda = self.angle_3 + return np.array( + [ + [ + np.cos(_theta / 2), + -np.exp(1j * _lambda) * np.sin(_theta / 2), + ], + [ + np.exp(1j * _phi) * np.sin(_theta / 2), + np.exp(1j * (_phi + _lambda)) * np.cos(_theta / 2), + ], + ] + ) + + def adjoint(self) -> list[Gate]: + return [U(-self.angle_1, -self.angle_3, -self.angle_2)] + + @staticmethod + def fixed_qubit_count() -> int: + return 1 + + def bind_values(self, **kwargs) -> TripleAngledGate: + return _get_angles(self, **kwargs) + + @staticmethod + @circuit.subroutine(register=True) + def u( + target: QubitSetInput, + angle_1: Union[FreeParameterExpression, float], + angle_2: Union[FreeParameterExpression, float], + angle_3: Union[FreeParameterExpression, float], + *, + control: Optional[QubitSetInput] = None, + control_state: Optional[BasisStateInput] = None, + power: float = 1, + ) -> Iterable[Instruction]: + r"""Generalized single-qubit rotation gate. + + Unitary matrix: + + .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} + \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} + \end{bmatrix}. + + Args: + target (QubitSetInput): Target qubit(s) + angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. + angle_2 (Union[FreeParameterExpression, float]): phi angle in radians. + angle_3 (Union[FreeParameterExpression, float]): lambda angle in radians. + control (Optional[QubitSetInput]): Control qubit(s). Default None. + control_state (Optional[BasisStateInput]): Quantum state on which to control the + operation. Must be a binary sequence of same length as number of qubits in + `control`. Will be ignored if `control` is not present. May be represented as a + string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent + controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being + in the \\|1⟩ state. Default "1" * len(control). + power (float): Integer or fractional power to raise the gate to. Negative + powers will be split into an inverse, accompanied by the positive power. + Default 1. + + Returns: + Iterable[Instruction]: U instruction. + + Examples: + >>> circ = Circuit().u(0, 0.15, 0.34, 0.52) + """ + return [ + Instruction( + U(angle_1, angle_2, angle_3), + target=qubit, + control=control, + control_state=control_state, + power=power, + ) + for qubit in QubitSet(target) + ] + + +Gate.register_gate(U) + + # Two qubit gates # diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index 7cd8e0a9d..6e87db78d 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -19,6 +19,7 @@ from typing import Any, NamedTuple, Union from braket.circuits.compiler_directive import CompilerDirective +from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.noise import Noise from braket.registers.qubit import Qubit @@ -42,6 +43,7 @@ class MomentType(str, Enum): INITIALIZATION_NOISE = "initialization_noise" READOUT_NOISE = "readout_noise" COMPILER_DIRECTIVE = "compiler_directive" + GLOBAL_PHASE = "global_phase" class MomentsKey(NamedTuple): @@ -59,6 +61,7 @@ class MomentsKey(NamedTuple): qubits: QubitSet moment_type: MomentType noise_index: int + subindex: int = 0 class Moments(Mapping[MomentsKey, Instruction]): @@ -106,6 +109,7 @@ def __init__(self, instructions: Iterable[Instruction] | None = None): self._qubits = QubitSet() self._depth = 0 self._time_all_qubits = -1 + self._number_gphase_in_current_moment = 0 self.add(instructions or []) @@ -181,6 +185,17 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._time_all_qubits = time elif isinstance(operator, Noise): self.add_noise(instruction) + elif isinstance(operator, Gate) and operator.name == "GPhase": + time = self._get_qubit_times(self._max_times.keys()) + 1 + self._number_gphase_in_current_moment += 1 + key = MomentsKey( + time, + QubitSet([]), + MomentType.GLOBAL_PHASE, + 0, + self._number_gphase_in_current_moment, + ) + self._moments[key] = instruction else: qubit_range = instruction.target.union(instruction.control) time = self._update_qubit_times(qubit_range) @@ -188,14 +203,15 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._qubits.update(qubit_range) self._depth = max(self._depth, time + 1) + def _get_qubit_times(self, qubits: QubitSet) -> int: + return max([self._max_time_for_qubit(qubit) for qubit in qubits] + [self._time_all_qubits]) + def _update_qubit_times(self, qubits: QubitSet) -> int: - qubit_max_times = [self._max_time_for_qubit(qubit) for qubit in qubits] + [ - self._time_all_qubits - ] - time = max(qubit_max_times) + 1 + time = self._get_qubit_times(qubits) + 1 # Update time for all specified qubits for qubit in qubits: self._max_times[qubit] = time + self._number_gphase_in_current_moment = 0 return time def add_noise( diff --git a/src/braket/circuits/translations.py b/src/braket/circuits/translations.py index ba536594f..bbb194be3 100644 --- a/src/braket/circuits/translations.py +++ b/src/braket/circuits/translations.py @@ -31,6 +31,7 @@ from braket.ir.jaqcd.program_v1 import Results BRAKET_GATES = { + "gphase": braket_gates.GPhase, "i": braket_gates.I, "h": braket_gates.H, "x": braket_gates.X, @@ -55,6 +56,7 @@ "rx": braket_gates.Rx, "ry": braket_gates.Ry, "rz": braket_gates.Rz, + "U": braket_gates.U, "swap": braket_gates.Swap, "iswap": braket_gates.ISwap, "pswap": braket_gates.PSwap, diff --git a/test/unit_tests/braket/circuits/test_angled_gate.py b/test/unit_tests/braket/circuits/test_angled_gate.py index e76756e29..4e093e5b4 100644 --- a/test/unit_tests/braket/circuits/test_angled_gate.py +++ b/test/unit_tests/braket/circuits/test_angled_gate.py @@ -31,7 +31,7 @@ def test_is_operator(angled_gate): def test_angle_is_none(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="angle must not be None"): AngledGate(qubit_count=1, ascii_symbols=["foo"], angle=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 3f3268f83..916bfb050 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -12,6 +12,7 @@ # language governing permissions and limitations under the License. import numpy as np +import pytest from braket.circuits import ( AsciiCircuitDiagram, @@ -29,6 +30,10 @@ def test_empty_circuit(): assert AsciiCircuitDiagram.build_diagram(Circuit()) == "" +def test_only_gphase_circuit(): + assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" + + def test_one_gate_one_qubit(): circ = Circuit().h(0) expected = ("T : |0|", " ", "q0 : -H-", "", "T : |0|") @@ -58,6 +63,35 @@ def test_one_gate_one_qubit_rotation_with_parameter(): _assert_correct_diagram(circ, expected) +@pytest.mark.parametrize("target", [0, 1]) +def test_one_gate_with_global_phase(target): + circ = Circuit().x(target=target).gphase(0.15) + expected = ( + "T : |0| 1 |", + "GP : |0|0.15|", + " ", + f"q{target} : -X------", + "", + "T : |0| 1 |", + "", + "Global phase: 0.15", + ) + _assert_correct_diagram(circ, expected) + + +def test_one_gate_with_zero_global_phase(): + circ = Circuit().gphase(-0.15).x(target=0).gphase(0.15) + expected = ( + "T : | 0 | 1 |", + "GP : |-0.15|0.00|", + " ", + "q0 : -X----------", + "", + "T : | 0 | 1 |", + ) + _assert_correct_diagram(circ, expected) + + def test_one_gate_one_qubit_rotation_with_unicode(): theta = FreeParameter("\u03B8") circ = Circuit().rx(angle=theta, target=0) @@ -74,6 +108,24 @@ def test_one_gate_one_qubit_rotation_with_unicode(): _assert_correct_diagram(circ, expected) +def test_one_gate_with_parametric_expression_global_phase_(): + theta = FreeParameter("\u03B8") + circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) + expected = ( + "T : |0| 1 | 2 |", + "GP : |0|2*θ|2*θ + 1.0|", + " ", + "q0 : -X-X-------------", + "", + "T : |0| 1 | 2 |", + "", + "Global phase: 2*θ + 1.0", + "", + "Unassigned parameters: [θ].", + ) + _assert_correct_diagram(circ, expected) + + def test_one_gate_one_qubit_rotation_with_parameter_assigned(): theta = FreeParameter("theta") circ = Circuit().rx(angle=theta, target=0) @@ -187,6 +239,38 @@ def test_connector_across_two_qubits(): _assert_correct_diagram(circ, expected) +def test_neg_control_qubits(): + circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) + expected = ( + "T : |0|", + " ", + "q0 : -N-", + " | ", + "q1 : -C-", + " | ", + "q2 : -X-", + "", + "T : |0|", + ) + _assert_correct_diagram(circ, expected) + + +def test_only_neg_control_qubits(): + circ = Circuit().x(2, control=[0, 1], control_state=0) + expected = ( + "T : |0|", + " ", + "q0 : -N-", + " | ", + "q1 : -N-", + " | ", + "q2 : -X-", + "", + "T : |0|", + ) + _assert_correct_diagram(circ, expected) + + def test_connector_across_three_qubits(): circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) expected = ( diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 71cff0de7..928cff757 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -1748,6 +1748,22 @@ def foo( inputs={}, ), ), + ( + Circuit().gphase(0.15).x(0), + OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[1] q;", + "gphase(0.15);", + "x q[0];", + "b[0] = measure q[0];", + ] + ), + inputs={}, + ), + ), ], ) def test_from_ir(expected_circuit, ir): @@ -2027,6 +2043,14 @@ def test_to_unitary_with_compiler_directives_returns_expected_unitary(): ) +def test_to_unitary_with_global_phase(): + circuit = Circuit().x(0) + circuit_unitary = np.array([[0, 1], [1, 0]]) + assert np.allclose(circuit.to_unitary(), circuit_unitary) + circuit = circuit.gphase(np.pi / 2) + assert np.allclose(circuit.to_unitary(), 1j * circuit_unitary) + + @pytest.mark.parametrize( "circuit,expected_unitary", [ @@ -2046,6 +2070,8 @@ def test_to_unitary_with_compiler_directives_returns_expected_unitary(): (Circuit().rx(0, 0.15), gates.Rx(0.15).to_matrix()), (Circuit().ry(0, 0.15), gates.Ry(0.15).to_matrix()), (Circuit().rz(0, 0.15), gates.Rz(0.15).to_matrix()), + (Circuit().u(0, 0.15, 0.16, 0.17), gates.U(0.15, 0.16, 0.17).to_matrix()), + (Circuit().gphase(0.15), gates.GPhase(0.15).to_matrix()), (Circuit().phaseshift(0, 0.15), gates.PhaseShift(0.15).to_matrix()), (Circuit().cnot(0, 1), gates.CNot().to_matrix()), (Circuit().cnot(0, 1).add_result_type(ResultType.StateVector()), gates.CNot().to_matrix()), @@ -3134,3 +3160,23 @@ def test_parametrized_pulse_circuit(user_defined_frame): def test_free_param_float_mix(): Circuit().ms(0, 1, 0.1, FreeParameter("theta")) + + +def test_circuit_with_global_phase(): + circuit = Circuit().gphase(0.15).x(0) + assert circuit.global_phase == 0.15 + + assert circuit.to_ir( + ir_type=IRType.OPENQASM, + serialization_properties=OpenQASMSerializationProperties( + qubit_reference_type=QubitReferenceType.PHYSICAL + ), + ).source == "\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "gphase(0.15);", + "x $0;", + "b[0] = measure $0;", + ] + ) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 05b98ade4..fc8fe7787 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -35,12 +35,21 @@ from braket.pulse import ArbitraryWaveform, Frame, Port, PulseSequence +class NoTarget: + pass + + class TripleAngle: pass +class SingleNegControlModifier: + pass + + testdata = [ (Gate.H, "h", ir.H, [SingleTarget], {}), + (Gate.GPhase, "gphase", None, [NoTarget, Angle], {}), (Gate.I, "i", ir.I, [SingleTarget], {}), (Gate.X, "x", ir.X, [SingleTarget], {}), (Gate.Y, "y", ir.Y, [SingleTarget], {}), @@ -54,6 +63,7 @@ class TripleAngle: (Gate.Rx, "rx", ir.Rx, [SingleTarget, Angle], {}), (Gate.Ry, "ry", ir.Ry, [SingleTarget, Angle], {}), (Gate.Rz, "rz", ir.Rz, [SingleTarget, Angle], {}), + (Gate.U, "u", None, [SingleTarget, TripleAngle], {}), (Gate.CNot, "cnot", ir.CNot, [SingleTarget, SingleControl], {}), (Gate.CV, "cv", ir.CV, [SingleTarget, SingleControl], {}), (Gate.CCNot, "ccnot", ir.CCNot, [SingleTarget, DoubleControl], {}), @@ -118,9 +128,11 @@ class TripleAngle: ] parameterizable_gates = [ + Gate.GPhase, Gate.Rx, Gate.Ry, Gate.Rz, + Gate.U, Gate.PhaseShift, Gate.PSwap, Gate.XX, @@ -147,6 +159,10 @@ class TripleAngle: ] +def no_target_valid_input(**kwargs): + return {} + + def single_target_valid_input(**kwargs): return {"target": 2} @@ -171,6 +187,10 @@ def single_control_valid_input(**kwargs): return {"control": 0} +def single_neg_control_valid_input(**kwargs): + return {"control": [0], "control_state": [0]} + + def double_control_valid_ir_input(**kwargs): return {"controls": [0, 1]} @@ -193,11 +213,13 @@ def two_dimensional_matrix_valid_input(**kwargs): valid_ir_switcher = { + "NoTarget": no_target_valid_input, "SingleTarget": single_target_valid_input, "DoubleTarget": double_target_valid_ir_input, "Angle": angle_valid_input, "TripleAngle": triple_angle_valid_input, "SingleControl": single_control_valid_input, + "SingleNegControlModifier": single_neg_control_valid_input, "DoubleControl": double_control_valid_ir_input, "MultiTarget": multi_target_valid_input, "TwoDimensionalMatrix": two_dimensional_matrix_valid_ir_input, @@ -232,9 +254,13 @@ def create_valid_subroutine_input(irsubclasses, **kwargs): def create_valid_target_input(irsubclasses): input = {} qubit_set = [] + control_qubit_set = [] + control_state = None # based on the concept that control goes first in target input for subclass in irsubclasses: - if subclass == SingleTarget: + if subclass == NoTarget: + qubit_set.extend(list(no_target_valid_input().values())) + elif subclass == SingleTarget: qubit_set.extend(list(single_target_valid_input().values())) elif subclass == DoubleTarget: qubit_set.extend(list(double_target_valid_ir_input().values())) @@ -242,6 +268,9 @@ def create_valid_target_input(irsubclasses): qubit_set.extend(list(multi_target_valid_input().values())) elif subclass == SingleControl: qubit_set = list(single_control_valid_input().values()) + qubit_set + elif subclass == SingleNegControlModifier: + control_qubit_set = list(single_neg_control_valid_input()["control"]) + control_state = list(single_neg_control_valid_input()["control_state"]) elif subclass == DoubleControl: qubit_set = list(double_control_valid_ir_input().values()) + qubit_set elif subclass in (Angle, TwoDimensionalMatrix, TripleAngle): @@ -249,6 +278,8 @@ def create_valid_target_input(irsubclasses): else: raise ValueError("Invalid subclass") input["target"] = QubitSet(qubit_set) + input["control"] = QubitSet(control_qubit_set) + input["control_state"] = control_state return input @@ -282,7 +313,7 @@ def calculate_qubit_count(irsubclasses): qubit_count += 2 elif subclass == MultiTarget: qubit_count += 3 - elif subclass in (Angle, TwoDimensionalMatrix, TripleAngle): + elif subclass in (NoTarget, Angle, TwoDimensionalMatrix, TripleAngle): pass else: raise ValueError("Invalid subclass") @@ -434,6 +465,18 @@ def test_ir_gate_level(testclass, subroutine_name, irclass, irsubclasses, kwargs OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL), "rz(0.17) $4;", ), + ( + Gate.U(angle_1=0.17, angle_2=3.45, angle_3=5.21), + [4], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.VIRTUAL), + "U(0.17, 3.45, 5.21) q[4];", + ), + ( + Gate.U(angle_1=0.17, angle_2=3.45, angle_3=5.21), + [4], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL), + "U(0.17, 3.45, 5.21) $4;", + ), ( Gate.XX(angle=0.17), [4, 5], @@ -859,9 +902,53 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar subroutine_input = {"target": multi_targets} if Angle in irsubclasses: subroutine_input.update(angle_valid_input()) + if TripleAngle in irsubclasses: + subroutine_input.update(triple_angle_valid_input()) assert subroutine(**subroutine_input) == Circuit(instruction_list) +@pytest.mark.parametrize( + "control, control_state, instruction_set", + [ + ( + 2, + None, + Instruction(**create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle])), + ), + ( + 2, + [0], + [ + Instruction(operator=Gate.X(), target=2), + Instruction( + **create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle]) + ), + Instruction(operator=Gate.X(), target=2), + ], + ), + ( + [0, 2], + [0, 1], + Instruction( + **create_valid_instruction_input( + Gate.PhaseShift, [SingleTarget, SingleNegControlModifier, Angle] + ) + ), + ), + ], +) +def test_control_gphase_subroutine(control, control_state, instruction_set): + subroutine = getattr(Circuit(), "gphase") + assert subroutine(angle=0.123, control=control, control_state=control_state) == Circuit( + instruction_set + ) + + +def test_angle_gphase_is_none(): + with pytest.raises(ValueError, match="angle must not be None"): + Gate.GPhase(angle=None) + + @pytest.mark.parametrize("testclass,subroutine_name,irclass,irsubclasses,kwargs", testdata) def test_gate_adjoint_expansion_correct(testclass, subroutine_name, irclass, irsubclasses, kwargs): gate = testclass(**create_valid_gate_class_input(irsubclasses, **kwargs)) @@ -925,7 +1012,7 @@ def test_large_unitary(): @pytest.mark.parametrize("gate", parameterizable_gates) def test_bind_values(gate): - triple_angled = gate.__name__ in ("MS",) + triple_angled = gate.__name__ in ("MS", "U") num_params = 3 if triple_angled else 1 thetas = [FreeParameter(f"theta_{i}") for i in range(num_params)] mapping = {f"theta_{i}": i for i in range(num_params)} @@ -1070,6 +1157,13 @@ def test_pulse_gate_to_matrix(): "10", "negctrl @ ctrl @ negctrl @ z q[1], q[2], q[3], q[0];", ), + ( + Gate.GPhase(0.3), + QubitSet([]), + QubitSet([1]), + "1", + "ctrl @ gphase(0.3) q[1];", + ), ), ) def test_gate_control(gate, target, control, control_state, expected_ir): From 1b35781505061f5a328d9fcda658d928890713eb Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 21 Dec 2023 16:15:31 +0000 Subject: [PATCH 17/21] prepare release v1.65.0 --- CHANGELOG.md | 6 ++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc496e21..37503c397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.65.0 (2023-12-21) + +### Features + + * add U and GPhase gates + ## v1.64.2 (2023-12-19) ### Bug Fixes and Other Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index a30db2260..d21a3b476 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.64.3.dev0" +__version__ = "1.65.0" From 788b198ddc66dc395b9761d1f92e38524d6eeed2 Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 21 Dec 2023 16:15:31 +0000 Subject: [PATCH 18/21] update development version to v1.65.1.dev0 --- src/braket/_sdk/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index d21a3b476..ec7335e82 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.65.0" +__version__ = "1.65.1.dev0" From d5af538079d9cbb6f065da46e1e6731145e2a71b Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula <99367153+jcjaskula-aws@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:10:14 -0500 Subject: [PATCH 19/21] fix: validate out circuits that contain only non-zero-qubit gates (#842) * validate out all gphase circuits * update docstring * consider ctrl @ gphase * group error cases --- src/braket/circuits/circuit_helpers.py | 10 +++--- .../braket/circuits/test_circuit_helpers.py | 31 +++++++++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/braket/circuits/circuit_helpers.py b/src/braket/circuits/circuit_helpers.py index 44e9bd571..f0e3f3144 100644 --- a/src/braket/circuits/circuit_helpers.py +++ b/src/braket/circuits/circuit_helpers.py @@ -23,13 +23,15 @@ def validate_circuit_and_shots(circuit: Circuit, shots: int) -> None: shots (int): shots to validate Raises: - ValueError: If circuit has no instructions; if no result types - specified for circuit and `shots=0`. See `braket.circuit.result_types`; + ValueError: If circuit has no instructions; if circuit has a non-gphase instruction; if no + result types specified for circuit and `shots=0`. See `braket.circuit.result_types`; if circuit has observables that cannot be simultaneously measured and `shots>0`; or, if `StateVector` or `Amplitude` are specified as result types when `shots>0`. """ - if not circuit.instructions: - raise ValueError("Circuit must have instructions to run on a device") + if not circuit.instructions or all( + not (inst.target or inst.control) for inst in circuit.instructions + ): + raise ValueError("Circuit must have at least one non-zero-qubit gate to run on a device") if not shots and not circuit.result_types: raise ValueError( "No result types specified for circuit and shots=0. See `braket.circuits.result_types`" diff --git a/test/unit_tests/braket/circuits/test_circuit_helpers.py b/test/unit_tests/braket/circuits/test_circuit_helpers.py index 56f7fd2ec..8960325cb 100644 --- a/test/unit_tests/braket/circuits/test_circuit_helpers.py +++ b/test/unit_tests/braket/circuits/test_circuit_helpers.py @@ -18,17 +18,32 @@ def test_validate_circuit_and_shots_no_instructions(): - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match="Circuit must have at least one non-zero-qubit gate to run on a device" + ): validate_circuit_and_shots(Circuit(), 100) +def test_validate_circuit_and_shots_only_gphase(): + with pytest.raises( + ValueError, match="Circuit must have at least one non-zero-qubit gate to run on a device" + ): + validate_circuit_and_shots(Circuit().gphase(0.15), 100) + + +def test_validate_circuit_and_shots_ctrl_gphase(): + assert validate_circuit_and_shots(Circuit().gphase(0.15, control=[0]), 100) is None + + def test_validate_circuit_and_shots_0_no_instructions(): - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match="Circuit must have at least one non-zero-qubit gate to run on a device" + ): validate_circuit_and_shots(Circuit(), 0) def test_validate_circuit_and_shots_0_no_results(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="No result types specified for circuit and shots=0."): validate_circuit_and_shots(Circuit().h(0), 0) @@ -54,12 +69,16 @@ def test_validate_circuit_and_shots_100_results_mixed_result(): def test_validate_circuit_and_shots_100_result_state_vector(): - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match="StateVector or Amplitude cannot be specified when shots>0" + ): validate_circuit_and_shots(Circuit().h(0).state_vector(), 100) def test_validate_circuit_and_shots_100_result_amplitude(): - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match="StateVector or Amplitude cannot be specified when shots>0" + ): validate_circuit_and_shots(Circuit().h(0).amplitude(state=["0"]), 100) @@ -74,7 +93,7 @@ def test_validate_circuit_and_shots_0_noncommuting(): def test_validate_circuit_and_shots_100_noncommuting(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Observables cannot be sampled simultaneously"): validate_circuit_and_shots( Circuit() .h(0) From b7ab26041804770edf65decde60a2e5a73d92dcb Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 25 Dec 2023 16:16:49 +0000 Subject: [PATCH 20/21] prepare release v1.65.1 --- CHANGELOG.md | 6 ++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37503c397..47d87d33a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.65.1 (2023-12-25) + +### Bug Fixes and Other Changes + + * validate out circuits that contain only non-zero-qubit gates + ## v1.65.0 (2023-12-21) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index ec7335e82..ad26cf05f 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.65.1.dev0" +__version__ = "1.65.1" From 8018fcba7cb48724e3451c406176df8d6dcce3a6 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 25 Dec 2023 16:16:49 +0000 Subject: [PATCH 21/21] update development version to v1.65.2.dev0 --- src/braket/_sdk/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index ad26cf05f..278494287 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.65.1" +__version__ = "1.65.2.dev0"