From e2d5899b5a5e6619192939dd7c43745fa44e930d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:12:24 -0700 Subject: [PATCH 01/28] infra: bump actions/checkout from 4.0.0 to 4.1.0 (#711) --- .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 160e8e000..631b90360 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -16,7 +16,7 @@ jobs: check-code-format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 with: diff --git a/.github/workflows/dependent-tests.yml b/.github/workflows/dependent-tests.yml index 732600ba2..10752e27e 100644 --- a/.github/workflows/dependent-tests.yml +++ b/.github/workflows/dependent-tests.yml @@ -21,7 +21,7 @@ jobs: - amazon-braket-pennylane-plugin-python steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 with: diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index a26168c11..fbd9a8942 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -12,7 +12,7 @@ jobs: name: Build and publish distribution to PyPi runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 with: diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 591ffed15..1e316a8cc 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -24,7 +24,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 with: diff --git a/.github/workflows/twine-check.yml b/.github/workflows/twine-check.yml index 5a5966763..f73925f8a 100644 --- a/.github/workflows/twine-check.yml +++ b/.github/workflows/twine-check.yml @@ -14,7 +14,7 @@ jobs: name: Check long description runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 with: From c95fddfb8cc999269b0492395e6c234e86ed71d3 Mon Sep 17 00:00:00 2001 From: Viraj Chaudhari <72896239+virajvchaudhari@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:51:17 -0700 Subject: [PATCH 02/28] feat: add queue visibility information (#712) * feat: add queue position for tasks (#299) * feat: add queue position for tasks * update docstring for queue_position * update docstrings * update package_name * update to enums and dataclass * test: add integ test for task queue_position (#300) * refactor: dataclass and file naming for queue info (#301) * refactor: dataclass and file naming for queue info * apply suggestions * update: task queue position after refactor (#309) * update: task queue position after refactor * add queue_position type hint details, change order of info * feat: queue position for hybrid jobs (#302) * feat: queue position for hybrid jobs * handle message return * add docstring changes * update docstring, and return None * add context in dataclass * update docstrings * indent fix * test: add integ test for jobs queue position (#310) * feat: queue position for hybrid jobs * handle message return * add docstring changes * update docstring, and return None * add context in dataclass * test: add integ test for jobs queue position * remove comment * minor fix * remove unnecessary branching * fix docstring merge * remove dataclass redefinition after merge * feat: queue depth for devices (#306) * feat: queue depth for devices (dataclass version) * add unit-test for queue depth * modify docstrings, remove helper funcs * add more info to docstrings * minor test edit * docstrings * indent * update QueuePriority to QueueType * test: add integ test for queue_depth (#311) * refactor: job and quantum_task keywords for queue_depth (#312) * refactor: job and quantum_task keywords for queue_depth * Update src/braket/aws/queue_information.py Co-authored-by: Kshitij Chhabra --------- Co-authored-by: Kshitij Chhabra * deps: update boto3 version for queue visibility (#319) * sync: public-main changes into feature/queue_visibility (#320) * feat: add Aria2 enum (#653) Co-authored-by: Viraj Chaudhari <72896239+virajvchaudhari@users.noreply.github.com> Co-authored-by: Cody Wang * infra: bump actions/checkout from 3.6.0 to 4.0.0 (#696) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/f43a0e5ff2bd294095638e18286ca9a3d1956744...3df4ab11eba7bda6032a0b82a6bb43b11571feac) --- updated-dependencies: - dependency-name: actions/checkout 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> * prepare release v1.55.0 * update development version to v1.55.1.dev0 * Revert "update: restricting parameter names to not collide with ones we use for OpenQASM generation. (#675)" (#701) This reverts commit b158736a5f394fd709f4d12d6f5e0890d05dbbf6. * infra: update codeowner file to amazon-braket/braket-dev (#699) Co-authored-by: Abe Coull <85974725+math411@users.noreply.github.com> * doc: Replace aws org with amazon-braket (#705) * prepare release v1.55.1 * update development version to v1.55.2.dev0 * doc: change the sphinx requirement to be greater than 7.0.0 (#704) Co-authored-by: Cody Wang * doc: add code contributors to the readme (#703) Co-authored-by: Cody Wang * doc: Remove trailing backquotes (#706) * infra: update the pre-commit hook with linters (#678) * infra: update the pre-commit hook with linters and secrets check Co-authored-by: Abe Coull Co-authored-by: Cody Wang * prepare release v1.55.1.post0 * update development version to v1.55.2.dev0 --------- Signed-off-by: dependabot[bot] Co-authored-by: ashlhans <65787294+ashlhans@users.noreply.github.com> Co-authored-by: Cody Wang Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ci Co-authored-by: Milan <30416311+krneta@users.noreply.github.com> Co-authored-by: Angela Guo Co-authored-by: Abe Coull <85974725+math411@users.noreply.github.com> Co-authored-by: Abe Coull * Revert "sync: public-main changes into feature/queue_visibility (#320)" This reverts commit be6460cb0d5382cc52f0943f2dcb2cb29a6db8f4. * update github script * minor fix --------- Signed-off-by: dependabot[bot] Co-authored-by: Kshitij Chhabra Co-authored-by: ashlhans <65787294+ashlhans@users.noreply.github.com> Co-authored-by: Cody Wang Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Milan <30416311+krneta@users.noreply.github.com> Co-authored-by: Angela Guo Co-authored-by: Abe Coull <85974725+math411@users.noreply.github.com> Co-authored-by: Abe Coull --- setup.py | 2 +- src/braket/aws/aws_device.py | 49 +++++++++++ src/braket/aws/aws_quantum_job.py | 33 +++++++ src/braket/aws/aws_quantum_task.py | 35 ++++++++ src/braket/aws/aws_session.py | 6 +- src/braket/aws/queue_information.py | 85 +++++++++++++++++++ test/integ_tests/test_queue_information.py | 84 ++++++++++++++++++ test/unit_tests/braket/aws/test_aws_device.py | 19 ++++- .../braket/aws/test_aws_quantum_job.py | 22 +++++ .../braket/aws/test_aws_quantum_task.py | 49 +++++++++-- .../unit_tests/braket/aws/test_aws_session.py | 30 +++++-- 11 files changed, 398 insertions(+), 16 deletions(-) create mode 100644 src/braket/aws/queue_information.py create mode 100644 test/integ_tests/test_queue_information.py diff --git a/setup.py b/setup.py index f21f27c3d..4dff452cf 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ "setuptools", "backoff", "boltons", - "boto3>=1.22.3", + "boto3>=1.28.53", "nest-asyncio", "networkx", "numpy", diff --git a/src/braket/aws/aws_device.py b/src/braket/aws/aws_device.py index fbd493071..87f7de886 100644 --- a/src/braket/aws/aws_device.py +++ b/src/braket/aws/aws_device.py @@ -29,6 +29,7 @@ from braket.aws.aws_quantum_task import AwsQuantumTask from braket.aws.aws_quantum_task_batch import AwsQuantumTaskBatch from braket.aws.aws_session import AwsSession +from braket.aws.queue_information import QueueDepthInfo, QueueType from braket.circuits import Circuit, Gate, QubitSet from braket.circuits.gate_calibrations import GateCalibrations from braket.device_schema import DeviceCapabilities, ExecutionDay, GateModelQpuParadigmProperties @@ -667,6 +668,54 @@ def get_device_region(device_arn: str) -> str: "see 'https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html'" ) + def queue_depth(self) -> QueueDepthInfo: + """ + Task queue depth refers to the total number of quantum tasks currently waiting + to run on a particular device. + + Returns: + QueueDepthInfo: Instance of the QueueDepth class representing queue depth + information for quantum tasks and hybrid jobs. + Queue depth refers to the number of quantum tasks and hybrid jobs queued on a particular + device. The normal tasks refers to the quantum tasks not submitted via Hybrid Jobs. + Whereas, the priority tasks refers to the total number of quantum tasks waiting to run + submitted through Amazon Braket Hybrid Jobs. These tasks run before the normal tasks. + If the queue depth for normal or priority quantum tasks is greater than 4000, we display + their respective queue depth as '>4000'. Similarly, for hybrid jobs if there are more + than 1000 jobs queued on a device, display the hybrid jobs queue depth as '>1000'. + Additionally, for QPUs if hybrid jobs queue depth is 0, we display information about + priority and count of the running hybrid job. + + Example: + Queue depth information for a running job. + >>> device = AwsDevice(Device.Amazon.SV1) + >>> print(device.queue_depth()) + QueueDepthInfo(quantum_tasks={: '0', + : '1'}, jobs='0 (1 prioritized job(s) running)') + + If more than 4000 quantum tasks queued on a device. + >>> device = AwsDevice(Device.Amazon.DM1) + >>> print(device.queue_depth()) + QueueDepthInfo(quantum_tasks={: '>4000', + : '2000'}, jobs='100') + """ + metadata = self.aws_session.get_device(arn=self.arn) + queue_metadata = metadata.get("deviceQueueInfo") + queue_info = {} + + for response in queue_metadata: + queue_name = response.get("queue") + queue_priority = response.get("queuePriority") + queue_size = response.get("queueSize") + + if queue_name == "QUANTUM_TASKS_QUEUE": + priority_enum = QueueType(queue_priority) + queue_info.setdefault("quantum_tasks", {})[priority_enum] = queue_size + else: + queue_info["jobs"] = queue_size + + return QueueDepthInfo(**queue_info) + def refresh_gate_calibrations(self) -> Optional[GateCalibrations]: """ Refreshes the gate calibration data upon request. diff --git a/src/braket/aws/aws_quantum_job.py b/src/braket/aws/aws_quantum_job.py index 288f17257..3120384fe 100644 --- a/src/braket/aws/aws_quantum_job.py +++ b/src/braket/aws/aws_quantum_job.py @@ -27,6 +27,7 @@ from braket.aws import AwsDevice from braket.aws.aws_session import AwsSession +from braket.aws.queue_information import HybridJobQueueInfo from braket.jobs import logs from braket.jobs.config import ( CheckpointConfig, @@ -278,6 +279,38 @@ def state(self, use_cached_value: bool = False) -> str: """ return self.metadata(use_cached_value).get("status") + def queue_position(self) -> HybridJobQueueInfo: + """ + The queue position details for the hybrid job. + + Returns: + HybridJobQueueInfo: Instance of HybridJobQueueInfo class representing + the queue position information for the hybrid job. The queue_position is + only returned when the hybrid job is not in RUNNING/CANCELLING/TERMINAL states, + else queue_position is returned as None. If the queue position of the hybrid + job is greater than 15, we return '>15' as the queue_position return value. + + Examples: + job status = QUEUED and position is 2 in the queue. + >>> job.queue_position() + HybridJobQueueInfo(queue_position='2', message=None) + + job status = QUEUED and position is 18 in the queue. + >>> job.queue_position() + HybridJobQueueInfo(queue_position='>15', message=None) + + job status = COMPLETED + >>> job.queue_position() + HybridJobQueueInfo(queue_position=None, + message='Job is in COMPLETED status. AmazonBraket does + not show queue position for this status.') + """ + response = self.metadata()["queueInfo"] + queue_position = None if response.get("position") == "None" else response.get("position") + message = response.get("message") + + return HybridJobQueueInfo(queue_position=queue_position, message=message) + def logs(self, wait: bool = False, poll_interval_seconds: int = 5) -> None: """Display logs for a given hybrid job, optionally tailing them until hybrid job is complete. diff --git a/src/braket/aws/aws_quantum_task.py b/src/braket/aws/aws_quantum_task.py index a4538cdd9..57fecb26c 100644 --- a/src/braket/aws/aws_quantum_task.py +++ b/src/braket/aws/aws_quantum_task.py @@ -24,6 +24,7 @@ from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation from braket.annealing.problem import Problem from braket.aws.aws_session import AwsSession +from braket.aws.queue_information import QuantumTaskQueueInfo, QueueType from braket.circuits import Instruction from braket.circuits.circuit import Circuit, Gate, QubitSet from braket.circuits.circuit_helpers import validate_circuit_and_shots @@ -314,6 +315,40 @@ def state(self, use_cached_value: bool = False) -> str: """ return self._status(use_cached_value) + def queue_position(self) -> QuantumTaskQueueInfo: + """ + The queue position details for the quantum task. + + Returns: + QuantumTaskQueueInfo: Instance of QuantumTaskQueueInfo class + representing the queue position information for the quantum task. + The queue_position is only returned when quantum task is not in + RUNNING/CANCELLING/TERMINAL states, else queue_position is returned as None. + The normal tasks refers to the quantum tasks not submitted via Hybrid Jobs. + Whereas, the priority tasks refers to the total number of quantum tasks waiting to run + submitted through Amazon Braket Hybrid Jobs. These tasks run before the normal tasks. + If the queue position for normal or priority quantum tasks is greater than 2000, + we display their respective queue position as '>2000'. + + Examples: + task status = QUEUED and queue position is 2050 + >>> task.queue_position() + QuantumTaskQueueInfo(queue_type=, + queue_position='>2000', message=None) + + task status = COMPLETED + >>> task.queue_position() + QuantumTaskQueueInfo(queue_type=, + queue_position=None, message='Task is in COMPLETED status. AmazonBraket does + not show queue position for this status.') + """ + response = self.metadata()["queueInfo"] + queue_type = QueueType(response["queuePriority"]) + queue_position = None if response.get("position") == "None" else response.get("position") + message = response.get("message") + + return QuantumTaskQueueInfo(queue_type, queue_position, message) + def _status(self, use_cached_value: bool = False) -> str: metadata = self.metadata(use_cached_value) status = metadata.get("status") diff --git a/src/braket/aws/aws_session.py b/src/braket/aws/aws_session.py index 300aee13e..5ce04a826 100644 --- a/src/braket/aws/aws_session.py +++ b/src/braket/aws/aws_session.py @@ -279,7 +279,9 @@ def get_quantum_task(self, arn: str) -> Dict[str, Any]: Returns: Dict[str, Any]: The response from the Amazon Braket `GetQuantumTask` operation. """ - response = self.braket_client.get_quantum_task(quantumTaskArn=arn) + response = self.braket_client.get_quantum_task( + quantumTaskArn=arn, additionalAttributeNames=["QueueInfo"] + ) broadcast_event(_TaskStatusEvent(arn=response["quantumTaskArn"], status=response["status"])) return response @@ -324,7 +326,7 @@ def get_job(self, arn: str) -> Dict[str, Any]: Returns: Dict[str, Any]: The response from the Amazon Braket `GetQuantumJob` operation. """ - return self.braket_client.get_job(jobArn=arn) + return self.braket_client.get_job(jobArn=arn, additionalAttributeNames=["QueueInfo"]) def cancel_job(self, arn: str) -> Dict[str, Any]: """ diff --git a/src/braket/aws/queue_information.py b/src/braket/aws/queue_information.py new file mode 100644 index 000000000..d45ed8761 --- /dev/null +++ b/src/braket/aws/queue_information.py @@ -0,0 +1,85 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from dataclasses import dataclass +from enum import Enum +from typing import Dict, Optional + + +class QueueType(str, Enum): + """ + Enumerates the possible priorities for the queue. + + Values: + NORMAL: Represents normal queue for the device. + PRIORITY: Represents priority queue for the device. + """ + + NORMAL = "Normal" + PRIORITY = "Priority" + + +@dataclass() +class QueueDepthInfo: + """ + Represents quantum tasks and hybrid jobs queue depth information. + + Attributes: + quantum_tasks (Dict[QueueType, str]): number of quantum tasks waiting + to run on a device. This includes both 'Normal' and 'Priority' tasks. + For Example, {'quantum_tasks': {QueueType.NORMAL: '7', QueueType.PRIORITY: '3'}} + jobs (str): number of hybrid jobs waiting to run on a device. Additionally, for QPUs if + hybrid jobs queue depth is 0, we display information about priority and count of the + running hybrid jobs. Example, 'jobs': '0 (1 prioritized job(s) running)' + """ + + quantum_tasks: Dict[QueueType, str] + jobs: str + + +@dataclass +class QuantumTaskQueueInfo: + """ + Represents quantum tasks queue information. + + Attributes: + queue_type (QueueType): type of the quantum_task queue either 'Normal' + or 'Priority'. + queue_position (Optional[str]): current position of your quantum task within a respective + device queue. This value can be None based on the state of the task. Default: None. + message (Optional[str]): Additional message information. This key is present only + if 'queue_position' is None. Default: None. + """ + + queue_type: QueueType + queue_position: Optional[str] = None + message: Optional[str] = None + + +@dataclass +class HybridJobQueueInfo: + """ + Represents hybrid job queue information. + + Attributes: + queue_position (Optional[str]): current position of your hybrid job within a respective + device queue. If the queue position of the hybrid job is greater than 15, we + return '>15' as the queue_position return value. The queue_position is only + returned when hybrid job is not in RUNNING/CANCELLING/TERMINAL states, else + queue_position is returned as None. + message (Optional[str]): Additional message information. This key is present only + if 'queue_position' is None. Default: None. + """ + + queue_position: Optional[str] = None + message: Optional[str] = None diff --git a/test/integ_tests/test_queue_information.py b/test/integ_tests/test_queue_information.py new file mode 100644 index 000000000..3398fde40 --- /dev/null +++ b/test/integ_tests/test_queue_information.py @@ -0,0 +1,84 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from braket.aws import AwsDevice, AwsQuantumJob +from braket.aws.queue_information import ( + HybridJobQueueInfo, + QuantumTaskQueueInfo, + QueueDepthInfo, + QueueType, +) +from braket.circuits import Circuit +from braket.devices import Devices + + +def test_task_queue_position(): + device = AwsDevice(Devices.Amazon.SV1) + + bell = Circuit().h(0).cnot(0, 1) + task = device.run(bell, shots=10) + + # call the queue_position method. + queue_information = task.queue_position() + + # data type validations + assert isinstance(queue_information, QuantumTaskQueueInfo) + assert isinstance(queue_information.queue_type, QueueType) + assert isinstance(queue_information.queue_position, (str, type(None))) + + # assert queue priority + assert queue_information.queue_type in [QueueType.NORMAL, QueueType.PRIORITY] + + # assert message + if queue_information.queue_position is None: + assert queue_information.message is not None + assert isinstance(queue_information.message, (str, type(None))) + else: + assert queue_information.message is None + + +def test_job_queue_position(aws_session): + job = AwsQuantumJob.create( + device=Devices.Amazon.SV1, + source_module="test/integ_tests/job_test_script.py", + entry_point="job_test_script:start_here", + aws_session=aws_session, + wait_until_complete=True, + hyperparameters={"test_case": "completed"}, + ) + + # call the queue_position method. + queue_information = job.queue_position() + + # data type validations + assert isinstance(queue_information, HybridJobQueueInfo) + + # assert message + assert queue_information.queue_position is None + assert isinstance(queue_information.message, str) + + +def test_queue_depth(): + device = AwsDevice(Devices.Amazon.SV1) + + # call the queue_depth method. + queue_information = device.queue_depth() + + # data type validations + assert isinstance(queue_information, QueueDepthInfo) + assert isinstance(queue_information.quantum_tasks, dict) + assert isinstance(queue_information.jobs, str) + + for key, value in queue_information.quantum_tasks.items(): + assert isinstance(key, QueueType) + assert isinstance(value, str) diff --git a/test/unit_tests/braket/aws/test_aws_device.py b/test/unit_tests/braket/aws/test_aws_device.py index 40abf6389..f279cac6a 100644 --- a/test/unit_tests/braket/aws/test_aws_device.py +++ b/test/unit_tests/braket/aws/test_aws_device.py @@ -34,6 +34,7 @@ from jsonschema import validate from braket.aws import AwsDevice, AwsDeviceType, AwsQuantumTask +from braket.aws.queue_information import QueueDepthInfo, QueueType from braket.circuits import Circuit, FreeParameter, Gate, QubitSet from braket.circuits.gate_calibrations import GateCalibrations from braket.device_schema.device_execution_window import DeviceExecutionWindow @@ -77,7 +78,6 @@ MOCK_GATE_MODEL_QPU_CAPABILITIES_JSON_1 ) - MOCK_gate_calibrations_JSON = { "gates": { "0": { @@ -218,6 +218,11 @@ def test_mock_rigetti_schema_1(): "providerName": "Rigetti", "deviceStatus": "OFFLINE", "deviceCapabilities": MOCK_GATE_MODEL_QPU_CAPABILITIES_1.json(), + "deviceQueueInfo": [ + {"queue": "QUANTUM_TASKS_QUEUE", "queueSize": "19", "queuePriority": "Normal"}, + {"queue": "QUANTUM_TASKS_QUEUE", "queueSize": "3", "queuePriority": "Priority"}, + {"queue": "JOBS_QUEUE", "queueSize": "0 (3 prioritized job(s) running)"}, + ], } MOCK_GATE_MODEL_QPU_CAPABILITIES_JSON_2 = { @@ -628,7 +633,6 @@ def test_device_refresh_metadata(arn): "nativeGateCalibrationsRef": "file://hostname/foo/bar", } - MOCK_PULSE_MODEL_QPU_PULSE_CAPABILITIES_JSON_2 = { "braketSchemaHeader": { "name": "braket.device_schema.pulse.pulse_device_action_properties", @@ -1937,3 +1941,14 @@ def test_parse_calibration_data_bad_instr(bad_input): ) device = AwsDevice(DWAVE_ARN, mock_session) device._parse_calibration_json(bad_input) + + +def test_queue_depth(arn): + mock_session = Mock() + mock_session.get_device.return_value = MOCK_GATE_MODEL_QPU_1 + mock_session.region = RIGETTI_REGION + device = AwsDevice(arn, mock_session) + assert device.queue_depth() == QueueDepthInfo( + quantum_tasks={QueueType.NORMAL: "19", QueueType.PRIORITY: "3"}, + jobs="0 (3 prioritized job(s) running)", + ) diff --git a/test/unit_tests/braket/aws/test_aws_quantum_job.py b/test/unit_tests/braket/aws/test_aws_quantum_job.py index cbc535a2a..ffc9bdb39 100644 --- a/test/unit_tests/braket/aws/test_aws_quantum_job.py +++ b/test/unit_tests/braket/aws/test_aws_quantum_job.py @@ -24,6 +24,7 @@ from botocore.exceptions import ClientError from braket.aws import AwsQuantumJob, AwsSession +from braket.aws.queue_information import HybridJobQueueInfo @pytest.fixture @@ -226,6 +227,27 @@ def test_metadata_caching(quantum_job, aws_session, generate_get_job_response, q assert aws_session.get_job.call_count == 1 +def test_queue_position(quantum_job, aws_session, generate_get_job_response): + state_1 = "COMPLETED" + queue_info = { + "queue": "JOBS_QUEUE", + "position": "None", + "message": "Job is in COMPLETED status. " + "AmazonBraket does not show queue position for this status.", + } + get_job_response_completed = generate_get_job_response(status=state_1, queueInfo=queue_info) + aws_session.get_job.return_value = get_job_response_completed + assert quantum_job.queue_position() == HybridJobQueueInfo( + queue_position=None, message=queue_info["message"] + ) + + state_2 = "QUEUED" + queue_info = {"queue": "JOBS_QUEUE", "position": "2"} + get_job_response_queued = generate_get_job_response(status=state_2, queueInfo=queue_info) + aws_session.get_job.return_value = get_job_response_queued + assert quantum_job.queue_position() == HybridJobQueueInfo(queue_position="2", message=None) + + def test_state(quantum_job, aws_session, generate_get_job_response, quantum_job_arn): state_1 = "RUNNING" get_job_response_running = generate_get_job_response(status=state_1) diff --git a/test/unit_tests/braket/aws/test_aws_quantum_task.py b/test/unit_tests/braket/aws/test_aws_quantum_task.py index de8ead78a..99270ad25 100644 --- a/test/unit_tests/braket/aws/test_aws_quantum_task.py +++ b/test/unit_tests/braket/aws/test_aws_quantum_task.py @@ -26,6 +26,7 @@ from braket.aws import AwsQuantumTask from braket.aws.aws_quantum_task import _create_annealing_device_params from braket.aws.aws_session import AwsSession +from braket.aws.queue_information import QuantumTaskQueueInfo, QueueType from braket.circuits import Circuit from braket.circuits.gates import PulseGate from braket.circuits.serialization import ( @@ -202,6 +203,23 @@ def test_metadata_call_if_none(quantum_task): quantum_task._aws_session.get_quantum_task.assert_called_with(quantum_task.id) +def test_queue_position(quantum_task): + state_1 = "QUEUED" + _mock_metadata(quantum_task._aws_session, state_1) + assert quantum_task.queue_position() == QuantumTaskQueueInfo( + queue_type=QueueType.NORMAL, queue_position="2", message=None + ) + + state_2 = "COMPLETED" + message = ( + f"'Task is in {state_2} status. AmazonBraket does not show queue position for this status.'" + ) + _mock_metadata(quantum_task._aws_session, state_2) + assert quantum_task.queue_position() == QuantumTaskQueueInfo( + queue_type=QueueType.NORMAL, queue_position=None, message=message + ) + + def test_state(quantum_task): state_1 = "RUNNING" _mock_metadata(quantum_task._aws_session, state_1) @@ -1097,11 +1115,32 @@ def _assert_create_quantum_task_called_with( def _mock_metadata(aws_session, state): - aws_session.get_quantum_task.return_value = { - "status": state, - "outputS3Bucket": S3_TARGET.bucket, - "outputS3Directory": S3_TARGET.key, - } + message = ( + f"'Task is in {state} status. AmazonBraket does not show queue position for this status.'" + ) + if state in AwsQuantumTask.TERMINAL_STATES or state in ["RUNNING", "CANCELLING"]: + aws_session.get_quantum_task.return_value = { + "status": state, + "outputS3Bucket": S3_TARGET.bucket, + "outputS3Directory": S3_TARGET.key, + "queueInfo": { + "queue": "QUANTUM_TASKS_QUEUE", + "position": "None", + "queuePriority": "Normal", + "message": message, + }, + } + else: + aws_session.get_quantum_task.return_value = { + "status": state, + "outputS3Bucket": S3_TARGET.bucket, + "outputS3Directory": S3_TARGET.key, + "queueInfo": { + "queue": "QUANTUM_TASKS_QUEUE", + "position": "2", + "queuePriority": "Normal", + }, + } def _mock_s3(aws_session, result): diff --git a/test/unit_tests/braket/aws/test_aws_session.py b/test/unit_tests/braket/aws/test_aws_session.py index 53423d99f..bfc65d54b 100644 --- a/test/unit_tests/braket/aws/test_aws_session.py +++ b/test/unit_tests/braket/aws/test_aws_session.py @@ -418,16 +418,20 @@ def test_create_quantum_task_with_job_token(aws_session): def test_get_quantum_task(aws_session): arn = "foo:bar:arn" status = "STATUS" + queue_info = ["QueueInfo"] return_value = {"quantumTaskArn": arn, "status": status} aws_session.braket_client.get_quantum_task.return_value = return_value assert aws_session.get_quantum_task(arn) == return_value - aws_session.braket_client.get_quantum_task.assert_called_with(quantumTaskArn=arn) + aws_session.braket_client.get_quantum_task.assert_called_with( + quantumTaskArn=arn, additionalAttributeNames=queue_info + ) def test_get_quantum_task_retry(aws_session, throttling_response, resource_not_found_response): arn = "foo:bar:arn" status = "STATUS" + queue_info = ["QueueInfo"] return_value = {"quantumTaskArn": arn, "status": status} aws_session.braket_client.get_quantum_task.side_effect = [ @@ -437,7 +441,9 @@ def test_get_quantum_task_retry(aws_session, throttling_response, resource_not_f ] assert aws_session.get_quantum_task(arn) == return_value - aws_session.braket_client.get_quantum_task.assert_called_with(quantumTaskArn=arn) + aws_session.braket_client.get_quantum_task.assert_called_with( + quantumTaskArn=arn, additionalAttributeNames=queue_info + ) assert aws_session.braket_client.get_quantum_task.call_count == 3 @@ -474,16 +480,20 @@ def test_get_quantum_task_does_not_retry_other_exceptions(aws_session): def test_get_job(aws_session, get_job_response): arn = "arn:aws:braket:us-west-2:1234567890:job/job-name" + queue_info = ["QueueInfo"] aws_session.braket_client.get_job.return_value = get_job_response assert aws_session.get_job(arn) == get_job_response - aws_session.braket_client.get_job.assert_called_with(jobArn=arn) + aws_session.braket_client.get_job.assert_called_with( + jobArn=arn, additionalAttributeNames=queue_info + ) def test_get_job_retry( aws_session, get_job_response, throttling_response, resource_not_found_response ): arn = "arn:aws:braket:us-west-2:1234567890:job/job-name" + queue_info = ["QueueInfo"] aws_session.braket_client.get_job.side_effect = [ ClientError(resource_not_found_response, "unit-test"), @@ -492,12 +502,15 @@ def test_get_job_retry( ] assert aws_session.get_job(arn) == get_job_response - aws_session.braket_client.get_job.assert_called_with(jobArn=arn) + aws_session.braket_client.get_job.assert_called_with( + jobArn=arn, additionalAttributeNames=queue_info + ) assert aws_session.braket_client.get_job.call_count == 3 def test_get_job_fail_after_retries(aws_session, throttling_response, resource_not_found_response): arn = "arn:aws:braket:us-west-2:1234567890:job/job-name" + queue_info = ["QueueInfo"] aws_session.braket_client.get_job.side_effect = [ ClientError(resource_not_found_response, "unit-test"), @@ -507,12 +520,15 @@ def test_get_job_fail_after_retries(aws_session, throttling_response, resource_n with pytest.raises(ClientError): aws_session.get_job(arn) - aws_session.braket_client.get_job.assert_called_with(jobArn=arn) + aws_session.braket_client.get_job.assert_called_with( + jobArn=arn, additionalAttributeNames=queue_info + ) assert aws_session.braket_client.get_job.call_count == 3 def test_get_job_does_not_retry_other_exceptions(aws_session): arn = "arn:aws:braket:us-west-2:1234567890:job/job-name" + queue_info = ["QueueInfo"] exception_response = { "Error": { "Code": "SomeOtherException", @@ -526,7 +542,9 @@ def test_get_job_does_not_retry_other_exceptions(aws_session): with pytest.raises(ClientError): aws_session.get_job(arn) - aws_session.braket_client.get_job.assert_called_with(jobArn=arn) + aws_session.braket_client.get_job.assert_called_with( + jobArn=arn, additionalAttributeNames=queue_info + ) assert aws_session.braket_client.get_job.call_count == 1 From 52286dc8b9fe8c347133443dfc3a881d886b0288 Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 26 Sep 2023 01:07:10 +0000 Subject: [PATCH 03/28] prepare release v1.56.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 cb5d60c81..e059789fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.56.0 (2023-09-26) + +### Features + + * add queue visibility information + ## v1.55.1.post0 (2023-09-18) ### Documentation Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index 865a962de..cc1069966 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.55.2.dev0" +__version__ = "1.56.0" From f015dd00b0dae4d203f74556b220993d2312027c Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 26 Sep 2023 01:07:10 +0000 Subject: [PATCH 04/28] update development version to v1.56.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 cc1069966..ed1952453 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.56.0" +__version__ = "1.56.1.dev0" From 1acf47684e988a59c3ef060594816acc2ebd05a2 Mon Sep 17 00:00:00 2001 From: Milan <30416311+krneta@users.noreply.github.com> Date: Tue, 26 Sep 2023 14:11:07 -0700 Subject: [PATCH 05/28] fix: fixing search device when don't have access to a region. (#708) --- src/braket/aws/aws_device.py | 42 +++++++----- test/unit_tests/braket/aws/test_aws_device.py | 68 +++++++++++++++++++ 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/braket/aws/aws_device.py b/src/braket/aws/aws_device.py index 87f7de886..d495dbe3f 100644 --- a/src/braket/aws/aws_device.py +++ b/src/braket/aws/aws_device.py @@ -17,6 +17,7 @@ import json import os import urllib.request +import warnings from datetime import datetime from enum import Enum from typing import Dict, List, Optional, Tuple, Union @@ -602,23 +603,32 @@ def get_devices( types_for_region = sorted( types if region == session_region else types - {AwsDeviceType.SIMULATOR} ) - region_device_arns = [ - result["deviceArn"] - for result in session_for_region.search_devices( - arns=arns, - names=names, - types=types_for_region, - statuses=statuses, - provider_names=provider_names, + try: + region_device_arns = [ + result["deviceArn"] + for result in session_for_region.search_devices( + arns=arns, + names=names, + types=types_for_region, + statuses=statuses, + provider_names=provider_names, + ) + ] + device_map.update( + { + arn: AwsDevice(arn, session_for_region) + for arn in region_device_arns + if arn not in device_map + } ) - ] - device_map.update( - { - arn: AwsDevice(arn, session_for_region) - for arn in region_device_arns - if arn not in device_map - } - ) + except ClientError as e: + error_code = e.response["Error"]["Code"] + warnings.warn( + f"{error_code}: Unable to search region '{region}' for devices." + " Please check your settings or try again later." + f" Continuing without devices in '{region}'." + ) + devices = list(device_map.values()) devices.sort(key=lambda x: getattr(x, order_by)) return devices diff --git a/test/unit_tests/braket/aws/test_aws_device.py b/test/unit_tests/braket/aws/test_aws_device.py index f279cac6a..c1e034834 100644 --- a/test/unit_tests/braket/aws/test_aws_device.py +++ b/test/unit_tests/braket/aws/test_aws_device.py @@ -1673,6 +1673,74 @@ def test_get_devices_simulators_only(mock_copy_session, aws_session): assert [result.name for result in results] == ["SV1"] +@patch("braket.aws.aws_device.AwsSession.copy_session") +def test_get_devices_with_error_in_region(mock_copy_session, aws_session): + aws_session.search_devices.side_effect = [ + # us-west-1 + [ + { + "deviceArn": SV1_ARN, + "deviceName": "SV1", + "deviceType": "SIMULATOR", + "deviceStatus": "ONLINE", + "providerName": "Amazon Braket", + } + ], + ValueError("should not be reachable"), + ] + aws_session.get_device.side_effect = [ + MOCK_GATE_MODEL_SIMULATOR, + ValueError("should not be reachable"), + ] + session_for_region = Mock() + session_for_region.search_devices.side_effect = [ + # us-east-1 + [ + { + "deviceArn": IONQ_ARN, + "deviceName": "IonQ Device", + "deviceType": "QPU", + "deviceStatus": "ONLINE", + "providerName": "IonQ", + }, + ], + # us-west-2 + ClientError( + { + "Error": { + "Code": "Test Code", + "Message": "Test Message", + } + }, + "Test Operation", + ), + # eu-west-2 + [ + { + "deviceArn": OQC_ARN, + "deviceName": "Lucy", + "deviceType": "QPU", + "deviceStatus": "ONLINE", + "providerName": "OQC", + } + ], + # Only two regions to search outside of current + ValueError("should not be reachable"), + ] + session_for_region.get_device.side_effect = [ + MOCK_GATE_MODEL_QPU_2, + MOCK_GATE_MODEL_QPU_3, + ValueError("should not be reachable"), + ] + mock_copy_session.return_value = session_for_region + # Search order: us-east-1, us-west-1, us-west-2, eu-west-2 + results = AwsDevice.get_devices( + statuses=["ONLINE"], + aws_session=aws_session, + ) + assert [result.name for result in results] == ["Blah", "Lucy", "SV1"] + + @pytest.mark.xfail(raises=ValueError) def test_get_devices_invalid_order_by(): AwsDevice.get_devices(order_by="foo") From dc67ca22fd506926ab2e3e27c9eef2c1486309f0 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 27 Sep 2023 16:17:17 +0000 Subject: [PATCH 06/28] prepare release v1.56.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 e059789fb..fb09fb1cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.56.1 (2023-09-27) + +### Bug Fixes and Other Changes + + * fixing search device when don't have access to a region. + ## v1.56.0 (2023-09-26) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index ed1952453..f3c84dd04 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.56.1.dev0" +__version__ = "1.56.1" From 4b690fdfe0a2625fe8bf7d0e7f837fe8a74ff2f4 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 27 Sep 2023 16:17:17 +0000 Subject: [PATCH 07/28] update development version to v1.56.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 f3c84dd04..f9bd793a2 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.56.1" +__version__ = "1.56.2.dev0" From 9cf49c2303caa75398c5ca870662425c1202fa0e Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Thu, 28 Sep 2023 15:54:19 -0400 Subject: [PATCH 08/28] Add documentation about decorators (#714) * Add documentation about decorators Co-authored-by: Tim Chen Co-authored-by: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> * Respond to CR --------- Co-authored-by: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> --- src/braket/experimental/autoqasm/README.md | 2 + .../experimental/autoqasm/doc/decorators.md | 131 ++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/braket/experimental/autoqasm/doc/decorators.md diff --git a/src/braket/experimental/autoqasm/README.md b/src/braket/experimental/autoqasm/README.md index 2584ba884..1bc1af135 100644 --- a/src/braket/experimental/autoqasm/README.md +++ b/src/braket/experimental/autoqasm/README.md @@ -98,6 +98,8 @@ task = device.run(my_bell_program, shots=100) result = task.result() ``` +Read more about AutoQASM decorators like `@aq.main` [here](doc/decorators.md). + For more example usage of AutoQASM, visit the [example notebooks](../../../../examples/autoqasm). ## Architecture diff --git a/src/braket/experimental/autoqasm/doc/decorators.md b/src/braket/experimental/autoqasm/doc/decorators.md new file mode 100644 index 000000000..494cb90c1 --- /dev/null +++ b/src/braket/experimental/autoqasm/doc/decorators.md @@ -0,0 +1,131 @@ +# AutoQASM decorators + +AutoQASM function decorators allow us to override the normal behavior of the decorated code. This is how we are able to hook into normal Python control flow statements and add them to the quantum program within our wrapped functions, for instance. + +There are a handful of decorators available through AutoQASM. Each one attaches its own special behaviors to the function it wraps. If you are new to AutoQASM, you can just use `@aq.main`! The other decorators unlock further capabilities, when you need it. + +## `@aq.main` + +This decorator marks the entry point to a quantum program. + +You can include gates, pulse control, classical control and subroutine calls. When you call the function wrapped by `@aq.main`, you will get a `Program` object. The `Program` object can be executed on [devices available through Amazon Braket](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html), including local simulators. The code snippet below creates a quantum program with `@aq.main` and runs it on the `Device` instantiated as `device`. + +``` +@aq.main(num_qubits=5) +def ghz_state(max_qubits): + """Create a GHZ state from a variable number of qubits.""" + h(0) + for i in aq.range(max_qubits): + cnot(0, i) + measure(list(range(max_qubits))) + +ghz_state_program = ghz_state(max_qubits=5) + +device.run(ghz_state_program) +``` + +When you run your quantum program, the Amazon Braket SDK automatically serializes the program to OpenQASM before sending it to the local simulator or the Amazon Braket service. In AutoQASM, you can optionally view the OpenQASM script of your quantum program before submitting to a device by calling `to_ir()` on the `Program` object. + +``` +print(ghz_state_program.to_ir()) +``` + +## `@aq.subroutine` + +This decorator declares a function to be a quantum program subroutine. + +Like any subroutine, `@aq.subroutine` is often used to simplify repeated code and increase the readability of a program. A subroutine must be called at least once from within an `@aq.main` function or another `@aq.subroutine` function in order to be included in a program. + +Because AutoQASM supports strongly-typed serialization formats such as OpenQASM, you must provide type hints for the inputs of your subroutine definitions. + +Our example below uses a subroutine to make Bell states on two pairs of qubits. +``` +@aq.subroutine +def bell(q0: int, q1: int) -> None: + h(q0) + cnot(q0, q1) + + +@aq.main(num_qubits=4) +def two_bell() -> None: + bell(0, 1) + bell(2, 3) + +two_bell_program = two_bell() +``` + +Let's take a look at the serialized output from `two_bell_program.to_ir()`, which shows that the modularity of the subroutine is preserved. + +``` +OPENQASM 3.0; +def bell(int[32] q0, int[32] q1) { + h __qubits__[q0]; + cnot __qubits__[q0], __qubits__[q1]; +} +qubit[4] __qubits__; +bell(0, 1); +bell(2, 3); +``` + +## `@aq.gate` + +Represents a gate definition. + +Gate definitions define higher-level gates in terms of other gates, and are often used to decompose a gate into the native gates of a device. + +The body of a gate definition can only contain gates. Qubits used in the body of a gate definition must be passed as input arguments, with the type hint `aq.Qubit`. Like subroutines, a gate definition must be called from within the context of a main quantum program or subroutine in order to be included in the program. + +``` +@aq.gate +def ch(q0: aq.Qubit, q1: aq.Qubit): + """Define a controlled-Hadamard gate.""" + ry(q1, -math.pi / 4) + cz(q0, q1) + ry(q1, math.pi / 4) + +@aq.main(num_qubits=2) +def main(): + h(0) + ch(0, 1) + +main_program = main() +``` + + +## `@aq.gate_calibration` + +This decorator allows you to register a calibration for a gate. A gate calibration is a device-specific, low-level, pulse implementation for a logical gate operation. + +At the pulse level, qubits are no longer interchangable. Each one has unique properties. Thus, a gate calibration is usually defined for a concrete set of qubits and parameters, but you can use input arguments to your function as well. + +The body of a function decorated with `@aq.gate_calibration` must only contain pulse operations. + +The first argument to the `@aq.gate_calibration` decorator must be the gate function that the calibration will be registered to. Concrete values for the qubits and parameters are supplied as keyword arguments to the decorator. +Every qubit and angle parameter of the gate being implemented must appear either as an argument to the `@aq.gate_calibration` decorator, or as a parameter of the decorated function. + +For example, the gate `rx` takes two arguments, target and angle. Each arguments must be either set in the decorator or declared as an input parameter to the decorated function. + +``` +# This calibration only applies to physical qubit zero, so we +# mark that in the decorator call +@aq.gate_calibration(implements=rx, target="$0") +def cal_1(angle: float): + # The calibration is applicable for any rotation angle, + # so we accept it as an input argument + pulse.barrier("$0") + pulse.shift_frequency(q0_rf_frame, -321047.14178613486) + pulse.play(q0_rf_frame, waveform(angle)) + pulse.shift_frequency(q0_rf_frame, 321047.14178613486) + pulse.barrier("$0") +``` + +To add the gate calibration to your program, use the `with_calibrations` method of the main program. + +``` +@aq.main +def my_program(): + rx("$0", 0.123) + measure("$0") + +my_program().with_calibrations([cal_1]) +``` From 4a28baf51d2671dcc71ddd46ccc4152d7c848487 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Thu, 28 Sep 2023 15:54:44 -0400 Subject: [PATCH 09/28] doc: Add FAQs to AutoQASM README (#713) * Add FAQs to AutoQASM README * Apply suggestions from code review Co-authored-by: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> * Respond to CR --------- Co-authored-by: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> --- src/braket/experimental/autoqasm/README.md | 65 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/braket/experimental/autoqasm/README.md b/src/braket/experimental/autoqasm/README.md index 1bc1af135..e2ab52f28 100644 --- a/src/braket/experimental/autoqasm/README.md +++ b/src/braket/experimental/autoqasm/README.md @@ -19,7 +19,13 @@ afraid of a few bugs, please keep on reading! AutoQASM provides a Pythonic developer experience for writing quantum programs. The working title "AutoQASM" is derived from the name of the [AutoGraph module of TensorFlow](https://www.tensorflow.org/api_docs/python/tf/autograph). AutoQASM uses AutoGraph to construct quantum assembly (QASM) programs rather than TensorFlow graphs. -AutoQASM provides a natural interface for expressing quantum programs with mid-circuit measurements and classical control flow using native Python language features. It allows the construction of modular programs consisting of common programming constructs such as loops and subroutines, and it preserves this modularity when serializing the program to OpenQASM. This enables a more imperative programming style than constructing programs via a series of function calls on a circuit object. +AutoQASM provides a natural interface for expressing quantum programs with mid-circuit measurements +and classical control flow using native Python language features. It allows the construction of +modular programs consisting of common programming constructs such as loops and subroutines. This +enables a more imperative programming style than constructing programs via a series of function calls +on a circuit object. + +AutoQASM programs can be serialized to OpenQASM. This textual representation for quantum programs is widely supported and enables interoperability among various frameworks. A crucial part of our serialization process is that modular structures within the program, such as loops and subroutines, are preserved when serializing to OpenQASM. Although it is still a work in progress, the intent is that AutoQASM will support any quantum programming paradigm which falls into the [OpenQASM 3.0](https://openqasm.com) language scope. AutoQASM supports serializing quantum programs to OpenQASM, which allows the programs to interoperate with any library or service that supports OpenQASM programs, such as Amazon Braket. @@ -138,10 +144,65 @@ Please tag your question with "Amazon Braket" and mention AutoQASM in the questi ## Tests -To run only AutoQASM tests (and skip the rest of the unit tests), run: +To run only AutoQASM tests (and skip the rest of the Amazon Braket SDK unit tests), run: ``` tox -e unit-tests -- test/unit_test/braket/experimental/autoqasm ``` Note that you may first need to run `pip install -e .[test]`. More information on running tests can be found in the [top-level README](../../../../README.md). + +## Frequently asked questions + +### 1. Will AutoQASM be extended to contain a library of quantum algorithms or quantum applications? + +No, we are focused on AutoQASM as an interface for low-level expression of +quantum programs: circuits, gates and pulses. Higher-level algorithm +libraries could be implemented using AutoQASM and benefit from modular +AutoQASM functionality such as subroutines. + +### 2. What is the relationship between AutoQASM and OpenQASM? + +AutoQASM is a quantum programming interface built in Python. +OpenQASM is a quantum assembly language, often used as a serialization format +for quantum programming frameworks and quantum hardware providers. We can +represent a quantum program equivalently in either format, but using AutoQASM +allows one to also make use of Python, including the Amazon Braket SDK. + +AutoQASM can be seen as implementing a builder pattern for OpenQASM. It +allows you serialize your program to OpenQASM with `Program.to_ir()`. The +interface is not strongly tied to OpenQASM, so we could serialize to other +formats in the future. + +### 3. What is the relationship between AutoQASM and the Amazon Braket SDK? + +AutoQASM lives alongside the Amazon Braket SDK as an experimental feature +branch. It supplements the program building experience and integrates with +Amazon Braket SDK features. For instance, one can build a program through +AutoQASM, and then use the SDK to run the program on a local simulator or on +an Amazon Braket device. + +### 4. Does AutoQASM support other providers beyond Amazon Braket? + +Yes. AutoQASM serializes to OpenQASM, and so it is applicable to any library +or QPU that supports OpenQASM. We do have features that use the Amazon Braket +SDK, such as [device-specific validation](../../../../examples/autoqasm/4_Native_programming.ipynb). +Because AutoQASM is open-source, anyone could +build similar integrations for another service. Reach out if you're +interested in doing this and would like support. + + +### 5. Does AutoQASM offer special support for device-specific programming? + +Yes, AutoQASM has device-specific validation to support native programming. +We plan to expand this functionality in the future. Learn more with our +[native programming example notebook](../../../../examples/autoqasm/4_Native_programming.ipynb). + +### 6. Do the devices available through Amazon Braket support all of AutoQASM's features? + +No, for example, the `reset` instruction is not supported by all devices. In +general, different QPUs and QHPs support different sets of features, so +AutoQASM will often support features that a particular device doesn't +support. We intend that AutoQASM will eventually be able to generate any +program representable by OpenQASM 3.0, with additional Python-side features +such as validation and visualization. From 98ea275456c6e4259a3e92f0ace86ff394ba3eae Mon Sep 17 00:00:00 2001 From: Kshitij Chhabra Date: Mon, 2 Oct 2023 21:26:21 -0700 Subject: [PATCH 10/28] fix: Refactor Qubit and QubitSet to a separate module (#717) * fix: Refactor Qubit and QubitSet to a separate module * Rename qubits module to registers --- src/braket/circuits/ascii_circuit_diagram.py | 2 +- src/braket/circuits/circuit.py | 4 +- src/braket/circuits/compiler_directive.py | 2 +- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gate_calibrations.py | 2 +- src/braket/circuits/gates.py | 4 +- src/braket/circuits/instruction.py | 4 +- src/braket/circuits/moments.py | 4 +- src/braket/circuits/noise.py | 2 +- src/braket/circuits/noise_helpers.py | 2 +- .../circuit_instruction_criteria.py | 2 +- .../noise_model/criteria_input_parsing.py | 2 +- .../circuits/noise_model/gate_criteria.py | 2 +- .../noise_model/initialization_criteria.py | 2 +- .../circuits/noise_model/noise_model.py | 2 +- .../noise_model/observable_criteria.py | 2 +- .../qubit_initialization_criteria.py | 2 +- .../noise_model/unitary_gate_criteria.py | 2 +- src/braket/circuits/noises.py | 4 +- src/braket/circuits/observable.py | 2 +- src/braket/circuits/observables.py | 2 +- src/braket/circuits/qubit.py | 56 +---------- src/braket/circuits/qubit_set.py | 81 +--------------- src/braket/circuits/result_type.py | 4 +- src/braket/circuits/result_types.py | 2 +- src/braket/circuits/unitary_calculation.py | 2 +- src/braket/pulse/pulse_sequence.py | 2 +- src/braket/registers/__init__.py | 15 +++ src/braket/registers/qubit.py | 68 ++++++++++++++ src/braket/registers/qubit_set.py | 93 +++++++++++++++++++ .../braket/circuits/test_noise_helpers.py | 2 +- .../pulse/ast/test_approximation_parser.py | 2 +- .../{circuits => registers}/test_qubit.py | 2 +- .../{circuits => registers}/test_qubit_set.py | 2 +- 34 files changed, 213 insertions(+), 170 deletions(-) create mode 100644 src/braket/registers/__init__.py create mode 100644 src/braket/registers/qubit.py create mode 100644 src/braket/registers/qubit_set.py rename test/unit_tests/braket/{circuits => registers}/test_qubit.py (97%) rename test/unit_tests/braket/{circuits => registers}/test_qubit_set.py (97%) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index f43f37a36..85de40a11 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -22,8 +22,8 @@ from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.noise import Noise -from braket.circuits.qubit_set import QubitSet from braket.circuits.result_type import ResultType +from braket.registers.qubit_set import QubitSet class AsciiCircuitDiagram(CircuitDiagram): diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 7e3bfcd4d..34bde9b98 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -39,8 +39,6 @@ from braket.circuits.observable import Observable from braket.circuits.observables import TensorProduct from braket.circuits.parameterizable import Parameterizable -from braket.circuits.qubit import QubitInput -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.result_type import ( ObservableParameterResultType, ObservableResultType, @@ -60,6 +58,8 @@ from braket.pulse import ArbitraryWaveform, Frame from braket.pulse.ast.qasm_parser import ast_to_qasm from braket.pulse.pulse_sequence import PulseSequence, _validate_uniqueness +from braket.registers.qubit import QubitInput +from braket.registers.qubit_set import QubitSet, QubitSetInput SubroutineReturn = TypeVar( "SubroutineReturn", Iterable[Instruction], Instruction, ResultType, Iterable[ResultType] diff --git a/src/braket/circuits/compiler_directive.py b/src/braket/circuits/compiler_directive.py index cd40a596c..3ca677ff7 100644 --- a/src/braket/circuits/compiler_directive.py +++ b/src/braket/circuits/compiler_directive.py @@ -16,8 +16,8 @@ from typing import Any, Sequence, Tuple from braket.circuits.operator import Operator -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import IRType, SerializationProperties +from braket.registers.qubit_set import QubitSet class CompilerDirective(Operator): diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 81f2499d1..135a892c3 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -18,12 +18,12 @@ from braket.circuits.basis_state import BasisState, BasisStateInput from braket.circuits.quantum_operator import QuantumOperator -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import ( IRType, OpenQASMSerializationProperties, SerializationProperties, ) +from braket.registers.qubit_set import QubitSet class Gate(QuantumOperator): diff --git a/src/braket/circuits/gate_calibrations.py b/src/braket/circuits/gate_calibrations.py index 01694a1e7..3c5abefd7 100644 --- a/src/braket/circuits/gate_calibrations.py +++ b/src/braket/circuits/gate_calibrations.py @@ -17,13 +17,13 @@ from typing import Any, Dict, List, Optional, Tuple from braket.circuits.gate import Gate -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import ( IRType, OpenQASMSerializationProperties, QubitReferenceType, ) from braket.pulse.pulse_sequence import PulseSequence +from braket.registers.qubit_set import QubitSet class GateCalibrations: diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index ac57d05b4..a68d9eade 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -39,11 +39,11 @@ is_unitary, verify_quantum_operator_matrix_dimensions, ) -from braket.circuits.qubit import QubitInput -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.serialization import OpenQASMSerializationProperties from braket.pulse.ast.qasm_parser import ast_to_qasm from braket.pulse.pulse_sequence import PulseSequence +from braket.registers.qubit import QubitInput +from braket.registers.qubit_set import QubitSet, QubitSetInput """ To add a new gate: diff --git a/src/braket/circuits/instruction.py b/src/braket/circuits/instruction.py index 148711c79..6211e1221 100644 --- a/src/braket/circuits/instruction.py +++ b/src/braket/circuits/instruction.py @@ -20,9 +20,9 @@ from braket.circuits.gate import Gate from braket.circuits.operator import Operator from braket.circuits.quantum_operator import QuantumOperator -from braket.circuits.qubit import QubitInput -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.serialization import IRType, SerializationProperties +from braket.registers.qubit import QubitInput +from braket.registers.qubit_set import QubitSet, QubitSetInput # InstructionOperator is a type alias, and it can be expanded to include other operators InstructionOperator = Operator diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index f1387d023..70cc81bcd 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -31,8 +31,8 @@ from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.instruction import Instruction from braket.circuits.noise import Noise -from braket.circuits.qubit import Qubit -from braket.circuits.qubit_set import QubitSet +from braket.registers.qubit import Qubit +from braket.registers.qubit_set import QubitSet class MomentType(str, Enum): diff --git a/src/braket/circuits/noise.py b/src/braket/circuits/noise.py index 5abf19f60..498f70fdc 100644 --- a/src/braket/circuits/noise.py +++ b/src/braket/circuits/noise.py @@ -21,12 +21,12 @@ from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.parameterizable import Parameterizable from braket.circuits.quantum_operator import QuantumOperator -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import ( IRType, OpenQASMSerializationProperties, SerializationProperties, ) +from braket.registers.qubit_set import QubitSet class Noise(QuantumOperator): diff --git a/src/braket/circuits/noise_helpers.py b/src/braket/circuits/noise_helpers.py index 29ade8cf9..2238a2d61 100644 --- a/src/braket/circuits/noise_helpers.py +++ b/src/braket/circuits/noise_helpers.py @@ -23,7 +23,7 @@ from braket.circuits.moments import Moments from braket.circuits.noise import Noise from braket.circuits.quantum_operator_helpers import is_unitary -from braket.circuits.qubit_set import QubitSet, QubitSetInput +from braket.registers.qubit_set import QubitSet, QubitSetInput if TYPE_CHECKING: # pragma: no cover from braket.circuits.circuit import Circuit diff --git a/src/braket/circuits/noise_model/circuit_instruction_criteria.py b/src/braket/circuits/noise_model/circuit_instruction_criteria.py index 2e5928857..0a8c11bdf 100644 --- a/src/braket/circuits/noise_model/circuit_instruction_criteria.py +++ b/src/braket/circuits/noise_model/circuit_instruction_criteria.py @@ -16,7 +16,7 @@ from braket.circuits.instruction import Instruction from braket.circuits.noise_model.criteria import Criteria -from braket.circuits.qubit_set import QubitSetInput +from braket.registers.qubit_set import QubitSetInput class CircuitInstructionCriteria(Criteria): diff --git a/src/braket/circuits/noise_model/criteria_input_parsing.py b/src/braket/circuits/noise_model/criteria_input_parsing.py index 193525f3b..a47d8d78c 100644 --- a/src/braket/circuits/noise_model/criteria_input_parsing.py +++ b/src/braket/circuits/noise_model/criteria_input_parsing.py @@ -14,7 +14,7 @@ from typing import Iterable, Optional, Set, Tuple, Union from braket.circuits.quantum_operator import QuantumOperator -from braket.circuits.qubit_set import QubitSetInput +from braket.registers.qubit_set import QubitSetInput def parse_operator_input( diff --git a/src/braket/circuits/noise_model/gate_criteria.py b/src/braket/circuits/noise_model/gate_criteria.py index 3505391d8..571c6cb61 100644 --- a/src/braket/circuits/noise_model/gate_criteria.py +++ b/src/braket/circuits/noise_model/gate_criteria.py @@ -21,7 +21,7 @@ parse_operator_input, parse_qubit_input, ) -from braket.circuits.qubit_set import QubitSetInput +from braket.registers.qubit_set import QubitSetInput class GateCriteria(CircuitInstructionCriteria): diff --git a/src/braket/circuits/noise_model/initialization_criteria.py b/src/braket/circuits/noise_model/initialization_criteria.py index 6229573bc..e40d4e9de 100644 --- a/src/braket/circuits/noise_model/initialization_criteria.py +++ b/src/braket/circuits/noise_model/initialization_criteria.py @@ -14,7 +14,7 @@ from abc import abstractmethod from braket.circuits.noise_model.criteria import Criteria -from braket.circuits.qubit_set import QubitSetInput +from braket.registers.qubit_set import QubitSetInput class InitializationCriteria(Criteria): diff --git a/src/braket/circuits/noise_model/noise_model.py b/src/braket/circuits/noise_model/noise_model.py index 76cdffa2a..b6805e338 100644 --- a/src/braket/circuits/noise_model/noise_model.py +++ b/src/braket/circuits/noise_model/noise_model.py @@ -25,8 +25,8 @@ from braket.circuits.noise_model.criteria import Criteria, CriteriaKey, CriteriaKeyResult from braket.circuits.noise_model.initialization_criteria import InitializationCriteria from braket.circuits.noise_model.result_type_criteria import ResultTypeCriteria -from braket.circuits.qubit_set import QubitSetInput from braket.circuits.result_types import ObservableResultType +from braket.registers.qubit_set import QubitSetInput @dataclass diff --git a/src/braket/circuits/noise_model/observable_criteria.py b/src/braket/circuits/noise_model/observable_criteria.py index e6affc94c..46c0e3e7c 100644 --- a/src/braket/circuits/noise_model/observable_criteria.py +++ b/src/braket/circuits/noise_model/observable_criteria.py @@ -20,8 +20,8 @@ ) from braket.circuits.noise_model.result_type_criteria import ResultTypeCriteria from braket.circuits.observable import Observable -from braket.circuits.qubit_set import QubitSetInput from braket.circuits.result_type import ObservableResultType, ResultType +from braket.registers.qubit_set import QubitSetInput class ObservableCriteria(ResultTypeCriteria): diff --git a/src/braket/circuits/noise_model/qubit_initialization_criteria.py b/src/braket/circuits/noise_model/qubit_initialization_criteria.py index 0646845ce..dade3a0e4 100644 --- a/src/braket/circuits/noise_model/qubit_initialization_criteria.py +++ b/src/braket/circuits/noise_model/qubit_initialization_criteria.py @@ -16,7 +16,7 @@ from braket.circuits.noise_model.criteria import Criteria, CriteriaKey, CriteriaKeyResult from braket.circuits.noise_model.criteria_input_parsing import parse_qubit_input from braket.circuits.noise_model.initialization_criteria import InitializationCriteria -from braket.circuits.qubit_set import QubitSet, QubitSetInput +from braket.registers.qubit_set import QubitSet, QubitSetInput class QubitInitializationCriteria(InitializationCriteria): diff --git a/src/braket/circuits/noise_model/unitary_gate_criteria.py b/src/braket/circuits/noise_model/unitary_gate_criteria.py index c87292980..7642a143e 100644 --- a/src/braket/circuits/noise_model/unitary_gate_criteria.py +++ b/src/braket/circuits/noise_model/unitary_gate_criteria.py @@ -18,7 +18,7 @@ from braket.circuits.noise_model.circuit_instruction_criteria import CircuitInstructionCriteria from braket.circuits.noise_model.criteria import Criteria, CriteriaKey, CriteriaKeyResult from braket.circuits.noise_model.criteria_input_parsing import parse_qubit_input -from braket.circuits.qubit_set import QubitSetInput +from braket.registers.qubit_set import QubitSetInput class UnitaryGateCriteria(CircuitInstructionCriteria): diff --git a/src/braket/circuits/noises.py b/src/braket/circuits/noises.py index 75cec6c42..11dac20e8 100644 --- a/src/braket/circuits/noises.py +++ b/src/braket/circuits/noises.py @@ -36,9 +36,9 @@ is_cptp, verify_quantum_operator_matrix_dimensions, ) -from braket.circuits.qubit import QubitInput -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.serialization import OpenQASMSerializationProperties +from braket.registers.qubit import QubitInput +from braket.registers.qubit_set import QubitSet, QubitSetInput """ To add a new Noise implementation: diff --git a/src/braket/circuits/observable.py b/src/braket/circuits/observable.py index c63304c91..6b70d3384 100644 --- a/src/braket/circuits/observable.py +++ b/src/braket/circuits/observable.py @@ -21,12 +21,12 @@ from braket.circuits.gate import Gate from braket.circuits.quantum_operator import QuantumOperator -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import ( IRType, OpenQASMSerializationProperties, SerializationProperties, ) +from braket.registers.qubit_set import QubitSet class Observable(QuantumOperator): diff --git a/src/braket/circuits/observables.py b/src/braket/circuits/observables.py index b27170230..9b1a4904b 100644 --- a/src/braket/circuits/observables.py +++ b/src/braket/circuits/observables.py @@ -29,8 +29,8 @@ is_hermitian, verify_quantum_operator_matrix_dimensions, ) -from braket.circuits.qubit_set import QubitSet from braket.circuits.serialization import IRType, OpenQASMSerializationProperties +from braket.registers.qubit_set import QubitSet class H(StandardObservable): diff --git a/src/braket/circuits/qubit.py b/src/braket/circuits/qubit.py index 479a453e3..f82e91fa0 100644 --- a/src/braket/circuits/qubit.py +++ b/src/braket/circuits/qubit.py @@ -11,58 +11,4 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from __future__ import annotations - -import numbers -from typing import Union - -QubitInput = Union["Qubit", int] - - -class Qubit(int): - """ - A quantum bit index. The index of this qubit is locally scoped towards the contained - circuit. This may not be the exact qubit index on the quantum device. - """ - - def __new__(cls, index: int): - """ - Args: - index (int): Index of the qubit. - - Raises: - ValueError: If `index` is less than zero. - - Examples: - >>> Qubit(0) - >>> Qubit(1) - """ - if not isinstance(index, numbers.Integral): - raise TypeError(f"Supplied qubit index, {index}, must be an integer.") - if index < 0: - raise ValueError(f"Supplied qubit index, {index}, cannot be less than zero.") - return super().__new__(cls, index) - - def __repr__(self): - return f"Qubit({super().__repr__()})" - - def __str__(self): - return self.__repr__() - - @staticmethod - def new(qubit: QubitInput) -> Qubit: - """ - Helper constructor - if input is a `Qubit` it returns the same value, - else a new `Qubit` is constructed. - - Args: - qubit (QubitInput): `Qubit` index. If `type == Qubit` then the `qubit` is returned. - - Returns: - Qubit: The qubit. - """ - - if isinstance(qubit, Qubit): - return qubit - else: - return Qubit(qubit) +from braket.registers import Qubit, QubitInput # noqa: F401 diff --git a/src/braket/circuits/qubit_set.py b/src/braket/circuits/qubit_set.py index d4571e91a..b2c1bbfc3 100644 --- a/src/braket/circuits/qubit_set.py +++ b/src/braket/circuits/qubit_set.py @@ -11,83 +11,4 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from __future__ import annotations - -from typing import Any, Dict, Iterable, Union - -from boltons.setutils import IndexedSet - -from braket.circuits.qubit import Qubit, QubitInput - -QubitSetInput = Union[QubitInput, Iterable[QubitInput]] - - -def _flatten(other: Any) -> Any: - if isinstance(other, Iterable) and not isinstance(other, str): - for item in other: - yield from _flatten(item) - else: - yield other - - -class QubitSet(IndexedSet): - """ - An ordered, unique set of quantum bits. - - Note: - QubitSet implements `__hash__()` but is a mutable object, therefore be careful when - mutating this object. - """ - - def __init__(self, qubits: QubitSetInput = None): - """ - Args: - qubits (QubitSetInput): Qubits to be included in the `QubitSet`. Default is `None`. - - Examples: - >>> qubits = QubitSet([0, 1]) - >>> for qubit in qubits: - ... print(qubit) - ... - Qubit(0) - Qubit(1) - - >>> qubits = QubitSet([0, 1, [2, 3]]) - >>> for qubit in qubits: - ... print(qubit) - ... - Qubit(0) - Qubit(1) - Qubit(2) - Qubit(3) - """ - - _qubits = [Qubit.new(qubit) for qubit in _flatten(qubits)] if qubits is not None else None - super().__init__(_qubits) - - def map(self, mapping: Dict[QubitInput, QubitInput]) -> QubitSet: - """ - Creates a new `QubitSet` where this instance's qubits are mapped to the values in `mapping`. - If this instance contains a qubit that is not in the `mapping` that qubit is not modified. - - Args: - mapping (Dict[QubitInput, QubitInput]): A dictionary of qubit mappings to - apply. Key is the qubit in this instance to target, and the value is what - the key will be changed to. - - Returns: - QubitSet: A new QubitSet with the `mapping` applied. - - Examples: - >>> qubits = QubitSet([0, 1]) - >>> mapping = {0: 10, Qubit(1): Qubit(11)} - >>> qubits.map(mapping) - QubitSet([Qubit(10), Qubit(11)]) - """ - - new_qubits = [mapping.get(qubit, qubit) for qubit in self] - - return QubitSet(new_qubits) - - def __hash__(self): - return hash(tuple(self)) +from braket.registers.qubit_set import QubitSet, QubitSetInput # noqa: F401 diff --git a/src/braket/circuits/result_type.py b/src/braket/circuits/result_type.py index d711e273b..787d9ee0d 100644 --- a/src/braket/circuits/result_type.py +++ b/src/braket/circuits/result_type.py @@ -18,13 +18,13 @@ from braket.circuits.free_parameter import FreeParameter from braket.circuits.observable import Observable from braket.circuits.observables import Sum -from braket.circuits.qubit import QubitInput -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.serialization import ( IRType, OpenQASMSerializationProperties, SerializationProperties, ) +from braket.registers.qubit import QubitInput +from braket.registers.qubit_set import QubitSet, QubitSetInput class ResultType: diff --git a/src/braket/circuits/result_types.py b/src/braket/circuits/result_types.py index d73c7fbda..25ff63716 100644 --- a/src/braket/circuits/result_types.py +++ b/src/braket/circuits/result_types.py @@ -22,13 +22,13 @@ from braket.circuits.free_parameter import FreeParameter from braket.circuits.observable import Observable from braket.circuits.observables import Sum -from braket.circuits.qubit_set import QubitSet, QubitSetInput from braket.circuits.result_type import ( ObservableParameterResultType, ObservableResultType, ResultType, ) from braket.circuits.serialization import IRType, OpenQASMSerializationProperties +from braket.registers.qubit_set import QubitSet, QubitSetInput """ To add a new result type: diff --git a/src/braket/circuits/unitary_calculation.py b/src/braket/circuits/unitary_calculation.py index 628d230e9..2059976e9 100644 --- a/src/braket/circuits/unitary_calculation.py +++ b/src/braket/circuits/unitary_calculation.py @@ -19,8 +19,8 @@ from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction -from braket.circuits.qubit_set import QubitSet from braket.default_simulator.linalg_utils import multiply_matrix +from braket.registers.qubit_set import QubitSet def _einsum_subscripts(targets: QubitSet, qubit_count: int) -> str: diff --git a/src/braket/pulse/pulse_sequence.py b/src/braket/pulse/pulse_sequence.py index 3606bbd11..0a3dba154 100644 --- a/src/braket/pulse/pulse_sequence.py +++ b/src/braket/pulse/pulse_sequence.py @@ -22,7 +22,6 @@ from oqpy import BitVar, PhysicalQubits, Program from oqpy.timing import OQDurationLiteral -from braket.circuits.qubit_set import QubitSet from braket.parametric.free_parameter import FreeParameter from braket.parametric.free_parameter_expression import FreeParameterExpression from braket.parametric.parameterizable import Parameterizable @@ -36,6 +35,7 @@ from braket.pulse.frame import Frame from braket.pulse.pulse_sequence_trace import PulseSequenceTrace from braket.pulse.waveforms import Waveform +from braket.registers.qubit_set import QubitSet class PulseSequence: diff --git a/src/braket/registers/__init__.py b/src/braket/registers/__init__.py new file mode 100644 index 000000000..0f433333b --- /dev/null +++ b/src/braket/registers/__init__.py @@ -0,0 +1,15 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from braket.registers.qubit import Qubit, QubitInput # noqa: F401 +from braket.registers.qubit_set import QubitSet, QubitSetInput # noqa: F401 diff --git a/src/braket/registers/qubit.py b/src/braket/registers/qubit.py new file mode 100644 index 000000000..479a453e3 --- /dev/null +++ b/src/braket/registers/qubit.py @@ -0,0 +1,68 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from __future__ import annotations + +import numbers +from typing import Union + +QubitInput = Union["Qubit", int] + + +class Qubit(int): + """ + A quantum bit index. The index of this qubit is locally scoped towards the contained + circuit. This may not be the exact qubit index on the quantum device. + """ + + def __new__(cls, index: int): + """ + Args: + index (int): Index of the qubit. + + Raises: + ValueError: If `index` is less than zero. + + Examples: + >>> Qubit(0) + >>> Qubit(1) + """ + if not isinstance(index, numbers.Integral): + raise TypeError(f"Supplied qubit index, {index}, must be an integer.") + if index < 0: + raise ValueError(f"Supplied qubit index, {index}, cannot be less than zero.") + return super().__new__(cls, index) + + def __repr__(self): + return f"Qubit({super().__repr__()})" + + def __str__(self): + return self.__repr__() + + @staticmethod + def new(qubit: QubitInput) -> Qubit: + """ + Helper constructor - if input is a `Qubit` it returns the same value, + else a new `Qubit` is constructed. + + Args: + qubit (QubitInput): `Qubit` index. If `type == Qubit` then the `qubit` is returned. + + Returns: + Qubit: The qubit. + """ + + if isinstance(qubit, Qubit): + return qubit + else: + return Qubit(qubit) diff --git a/src/braket/registers/qubit_set.py b/src/braket/registers/qubit_set.py new file mode 100644 index 000000000..ea012bc27 --- /dev/null +++ b/src/braket/registers/qubit_set.py @@ -0,0 +1,93 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from __future__ import annotations + +from typing import Any, Dict, Iterable, Union + +from boltons.setutils import IndexedSet + +from braket.registers.qubit import Qubit, QubitInput + +QubitSetInput = Union[QubitInput, Iterable[QubitInput]] + + +def _flatten(other: Any) -> Any: + if isinstance(other, Iterable) and not isinstance(other, str): + for item in other: + yield from _flatten(item) + else: + yield other + + +class QubitSet(IndexedSet): + """ + An ordered, unique set of quantum bits. + + Note: + QubitSet implements `__hash__()` but is a mutable object, therefore be careful when + mutating this object. + """ + + def __init__(self, qubits: QubitSetInput = None): + """ + Args: + qubits (QubitSetInput): Qubits to be included in the `QubitSet`. Default is `None`. + + Examples: + >>> qubits = QubitSet([0, 1]) + >>> for qubit in qubits: + ... print(qubit) + ... + Qubit(0) + Qubit(1) + + >>> qubits = QubitSet([0, 1, [2, 3]]) + >>> for qubit in qubits: + ... print(qubit) + ... + Qubit(0) + Qubit(1) + Qubit(2) + Qubit(3) + """ + + _qubits = [Qubit.new(qubit) for qubit in _flatten(qubits)] if qubits is not None else None + super().__init__(_qubits) + + def map(self, mapping: Dict[QubitInput, QubitInput]) -> QubitSet: + """ + Creates a new `QubitSet` where this instance's qubits are mapped to the values in `mapping`. + If this instance contains a qubit that is not in the `mapping` that qubit is not modified. + + Args: + mapping (Dict[QubitInput, QubitInput]): A dictionary of qubit mappings to + apply. Key is the qubit in this instance to target, and the value is what + the key will be changed to. + + Returns: + QubitSet: A new QubitSet with the `mapping` applied. + + Examples: + >>> qubits = QubitSet([0, 1]) + >>> mapping = {0: 10, Qubit(1): Qubit(11)} + >>> qubits.map(mapping) + QubitSet([Qubit(10), Qubit(11)]) + """ + + new_qubits = [mapping.get(qubit, qubit) for qubit in self] + + return QubitSet(new_qubits) + + def __hash__(self): + return hash(tuple(self)) diff --git a/test/unit_tests/braket/circuits/test_noise_helpers.py b/test/unit_tests/braket/circuits/test_noise_helpers.py index 421cf69c0..f2c956eab 100644 --- a/test/unit_tests/braket/circuits/test_noise_helpers.py +++ b/test/unit_tests/braket/circuits/test_noise_helpers.py @@ -20,7 +20,7 @@ from braket.circuits.moments import Moments from braket.circuits.noise import Noise from braket.circuits.noise_helpers import apply_noise_to_gates, apply_noise_to_moments -from braket.circuits.qubit_set import QubitSet +from braket.registers.qubit_set import QubitSet invalid_data_noise_type = [Gate.X(), None, 1.5] invalid_data_target_gates_type = [[-1, "foo"], [1.5, None, -1], "X", [Gate.X, "CNot"]] diff --git a/test/unit_tests/braket/pulse/ast/test_approximation_parser.py b/test/unit_tests/braket/pulse/ast/test_approximation_parser.py index 786c6b7e6..c4199e81b 100644 --- a/test/unit_tests/braket/pulse/ast/test_approximation_parser.py +++ b/test/unit_tests/braket/pulse/ast/test_approximation_parser.py @@ -19,12 +19,12 @@ from openpulse import ast from oqpy import IntVar -from braket.circuits.qubit_set import QubitSet from braket.pulse import ArbitraryWaveform, ConstantWaveform, DragGaussianWaveform, GaussianWaveform from braket.pulse.ast.approximation_parser import _ApproximationParser from braket.pulse.frame import Frame from braket.pulse.port import Port from braket.pulse.pulse_sequence import PulseSequence +from braket.registers.qubit_set import QubitSet from braket.timings.time_series import TimeSeries, _all_close diff --git a/test/unit_tests/braket/circuits/test_qubit.py b/test/unit_tests/braket/registers/test_qubit.py similarity index 97% rename from test/unit_tests/braket/circuits/test_qubit.py rename to test/unit_tests/braket/registers/test_qubit.py index b961986c1..98f89cf8d 100644 --- a/test/unit_tests/braket/circuits/test_qubit.py +++ b/test/unit_tests/braket/registers/test_qubit.py @@ -14,7 +14,7 @@ import numpy as np import pytest -from braket.circuits import Qubit +from braket.registers import Qubit @pytest.fixture diff --git a/test/unit_tests/braket/circuits/test_qubit_set.py b/test/unit_tests/braket/registers/test_qubit_set.py similarity index 97% rename from test/unit_tests/braket/circuits/test_qubit_set.py rename to test/unit_tests/braket/registers/test_qubit_set.py index 4a7eda395..1fd8d7212 100644 --- a/test/unit_tests/braket/circuits/test_qubit_set.py +++ b/test/unit_tests/braket/registers/test_qubit_set.py @@ -13,7 +13,7 @@ import pytest -from braket.circuits import Qubit, QubitSet +from braket.registers import Qubit, QubitSet @pytest.fixture From 78808d1e1235d334e7a7041aa834898a5d40848c Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 3 Oct 2023 16:16:13 +0000 Subject: [PATCH 11/28] prepare release v1.56.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 fb09fb1cb..238a38e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.56.2 (2023-10-03) + +### Bug Fixes and Other Changes + + * Refactor Qubit and QubitSet to a separate module + ## v1.56.1 (2023-09-27) ### Bug Fixes and Other Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index f9bd793a2..3c8890fa1 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.56.2.dev0" +__version__ = "1.56.2" From bb05cae237de19b05766435f18a4abb20138fe5f Mon Sep 17 00:00:00 2001 From: ci Date: Tue, 3 Oct 2023 16:16:13 +0000 Subject: [PATCH 12/28] update development version to v1.56.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 3c8890fa1..b01e8f75b 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.56.2" +__version__ = "1.56.3.dev0" From 82ced098f92e5973639e8496d1df335f0e85f97b Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Tue, 3 Oct 2023 17:44:36 -0700 Subject: [PATCH 13/28] feat: job helper functions (#720) Co-authored-by: Matthew Beach <85963088+mbeach-aws@users.noreply.github.com> --- examples/job.py | 4 +- src/braket/jobs/__init__.py | 8 +++ src/braket/jobs/data_persistence.py | 10 +-- src/braket/jobs/environment_variables.py | 71 +++++++++++++++++++ test/integ_tests/job_test_script.py | 16 ++--- .../braket/jobs/test_environment_variables.py | 66 +++++++++++++++++ 6 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/braket/jobs/environment_variables.py create mode 100644 test/unit_tests/braket/jobs/test_environment_variables.py diff --git a/examples/job.py b/examples/job.py index fdb2cec61..f5e330d1a 100644 --- a/examples/job.py +++ b/examples/job.py @@ -16,11 +16,11 @@ from braket.aws import AwsDevice, AwsQuantumJob from braket.circuits import Circuit from braket.devices import Devices -from braket.jobs import save_job_result +from braket.jobs import get_job_device_arn, save_job_result def run_job(): - device = AwsDevice(os.environ.get("AMZN_BRAKET_DEVICE_ARN")) + device = AwsDevice(get_job_device_arn()) bell = Circuit().h(0).cnot(0, 1) num_tasks = 10 diff --git a/src/braket/jobs/__init__.py b/src/braket/jobs/__init__.py index e58ba16eb..478305df5 100644 --- a/src/braket/jobs/__init__.py +++ b/src/braket/jobs/__init__.py @@ -23,4 +23,12 @@ save_job_checkpoint, save_job_result, ) +from braket.jobs.environment_variables import ( # noqa: F401 + get_checkpoint_dir, + get_hyperparameters, + get_input_data_dir, + get_job_device_arn, + get_job_name, + get_results_dir, +) from braket.jobs.image_uris import Framework, retrieve_image # noqa: F401 diff --git a/src/braket/jobs/data_persistence.py b/src/braket/jobs/data_persistence.py index 198a7e225..8a04d0a57 100644 --- a/src/braket/jobs/data_persistence.py +++ b/src/braket/jobs/data_persistence.py @@ -11,9 +11,9 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -import os from typing import Any, Dict +from braket.jobs.environment_variables import get_checkpoint_dir, get_job_name, get_results_dir from braket.jobs.serialization import deserialize_values, serialize_values from braket.jobs_data import PersistedJobData, PersistedJobDataFormat @@ -49,8 +49,8 @@ def save_job_checkpoint( """ if not checkpoint_data: raise ValueError("The checkpoint_data argument cannot be empty.") - checkpoint_directory = os.environ["AMZN_BRAKET_CHECKPOINT_DIR"] - job_name = os.environ["AMZN_BRAKET_JOB_NAME"] + checkpoint_directory = get_checkpoint_dir() + job_name = get_job_name() checkpoint_file_path = ( f"{checkpoint_directory}/{job_name}_{checkpoint_file_suffix}.json" if checkpoint_file_suffix @@ -90,7 +90,7 @@ def load_job_checkpoint(job_name: str, checkpoint_file_suffix: str = "") -> Dict ValueError: If the data stored in the checkpoint file can't be deserialized (possibly due to corruption). """ - checkpoint_directory = os.environ["AMZN_BRAKET_CHECKPOINT_DIR"] + checkpoint_directory = get_checkpoint_dir() checkpoint_file_path = ( f"{checkpoint_directory}/{job_name}_{checkpoint_file_suffix}.json" if checkpoint_file_suffix @@ -128,7 +128,7 @@ def save_job_result( """ if not result_data: raise ValueError("The result_data argument cannot be empty.") - result_directory = os.environ["AMZN_BRAKET_JOB_RESULTS_DIR"] + result_directory = get_results_dir() result_path = f"{result_directory}/results.json" with open(result_path, "w") as f: serialized_data = serialize_values(result_data or {}, data_format) diff --git a/src/braket/jobs/environment_variables.py b/src/braket/jobs/environment_variables.py new file mode 100644 index 000000000..489b1f18e --- /dev/null +++ b/src/braket/jobs/environment_variables.py @@ -0,0 +1,71 @@ +import json +import os +from typing import Dict + + +def get_job_name() -> str: + """ + Get the name of the current job. + + Returns: + str: The name of the job if in a job, else an empty string. + """ + return os.getenv("AMZN_BRAKET_JOB_NAME", "") + + +def get_job_device_arn() -> str: + """ + Get the device ARN of the current job. If not in a job, default to "local:none/none". + + Returns: + str: The device ARN of the current job or "local:none/none". + """ + return os.getenv("AMZN_BRAKET_DEVICE_ARN", "local:none/none") + + +def get_input_data_dir(channel: str = "input") -> str: + """ + Get the job input data directory. + + Args: + channel (str): The name of the input channel. Default value + corresponds to the default input channel name, `input`. + + Returns: + str: The input directory, defaulting to current working directory. + """ + input_dir = os.getenv("AMZN_BRAKET_INPUT_DIR", ".") + if input_dir != ".": + return f"{input_dir}/{channel}" + return input_dir + + +def get_results_dir() -> str: + """ + Get the job result directory. + + Returns: + str: The results directory, defaulting to current working directory. + """ + return os.getenv("AMZN_BRAKET_JOB_RESULTS_DIR", ".") + + +def get_checkpoint_dir() -> str: + """ + Get the job checkpoint directory. + Returns: + str: The checkpoint directory, defaulting to current working directory. + """ + return os.getenv("AMZN_BRAKET_CHECKPOINT_DIR", ".") + + +def get_hyperparameters() -> Dict[str, str]: + """ + Get the job checkpoint directory. + Returns: + str: The checkpoint directory, defaulting to current working directory. + """ + if "AMZN_BRAKET_HP_FILE" in os.environ: + with open(os.getenv("AMZN_BRAKET_HP_FILE"), "r") as f: + return json.load(f) + return {} diff --git a/test/integ_tests/job_test_script.py b/test/integ_tests/job_test_script.py index 7071ffd62..1fd7a364f 100644 --- a/test/integ_tests/job_test_script.py +++ b/test/integ_tests/job_test_script.py @@ -11,19 +11,19 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -import json -import os - from braket.aws import AwsDevice from braket.circuits import Circuit -from braket.jobs import save_job_checkpoint, save_job_result +from braket.jobs import ( + get_hyperparameters, + get_job_device_arn, + save_job_checkpoint, + save_job_result, +) from braket.jobs_data import PersistedJobDataFormat def start_here(): - hp_file = os.environ["AMZN_BRAKET_HP_FILE"] - with open(hp_file, "r") as f: - hyperparameters = json.load(f) + hyperparameters = get_hyperparameters() if hyperparameters["test_case"] == "completed": completed_job_script() @@ -40,7 +40,7 @@ def completed_job_script(): print("Test job started!!!!!") # Use the device declared in the Orchestration Script - device = AwsDevice(os.environ["AMZN_BRAKET_DEVICE_ARN"]) + device = AwsDevice(get_job_device_arn()) bell = Circuit().h(0).cnot(0, 1) for count in range(5): diff --git a/test/unit_tests/braket/jobs/test_environment_variables.py b/test/unit_tests/braket/jobs/test_environment_variables.py new file mode 100644 index 000000000..2c8a2f546 --- /dev/null +++ b/test/unit_tests/braket/jobs/test_environment_variables.py @@ -0,0 +1,66 @@ +import json +import os +import tempfile +from pathlib import Path +from unittest.mock import patch + +from braket.jobs import ( + get_checkpoint_dir, + get_hyperparameters, + get_input_data_dir, + get_job_device_arn, + get_job_name, + get_results_dir, +) + + +def test_job_name(): + assert get_job_name() == "" + job_name = "my_job_name" + with patch.dict(os.environ, {"AMZN_BRAKET_JOB_NAME": job_name}): + assert get_job_name() == job_name + + +def test_job_device_arn(): + assert get_job_device_arn() == "local:none/none" + device_arn = "my_device_arn" + with patch.dict(os.environ, {"AMZN_BRAKET_DEVICE_ARN": device_arn}): + assert get_job_device_arn() == device_arn + + +def test_input_data_dir(): + assert get_input_data_dir() == "." + input_path = "my/input/path" + with patch.dict(os.environ, {"AMZN_BRAKET_INPUT_DIR": input_path}): + assert get_input_data_dir() == f"{input_path}/input" + channel_name = "my_channel" + assert get_input_data_dir(channel_name) == f"{input_path}/{channel_name}" + + +def test_results_dir(): + assert get_results_dir() == "." + results_dir = "my_results_dir" + with patch.dict(os.environ, {"AMZN_BRAKET_JOB_RESULTS_DIR": results_dir}): + assert get_results_dir() == results_dir + + +def test_checkpoint_dir(): + assert get_checkpoint_dir() == "." + checkpoint_dir = "my_checkpoint_dir" + with patch.dict(os.environ, {"AMZN_BRAKET_CHECKPOINT_DIR": checkpoint_dir}): + assert get_checkpoint_dir() == checkpoint_dir + + +def test_hyperparameters(): + assert get_hyperparameters() == {} + hp_file = "my_hyperparameters.json" + hyperparameters = { + "a": "a_val", + "b": 2, + } + with tempfile.TemporaryDirectory() as temp_dir, patch.dict( + os.environ, {"AMZN_BRAKET_HP_FILE": str(Path(temp_dir) / hp_file)} + ): + with open(str(Path(temp_dir) / hp_file), "w") as f: + json.dump(hyperparameters, f) + assert get_hyperparameters() == hyperparameters From 5c1e4e7dd7389416d5aaf5f6e9388a79c441d53d Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 4 Oct 2023 00:52:43 -0700 Subject: [PATCH 14/28] feat: wrap non-dict results and update results on subsequent calls (#721) --- src/braket/aws/aws_quantum_job.py | 13 +-- src/braket/jobs/data_persistence.py | 80 +++++++++++++++---- .../braket/jobs/test_data_persistence.py | 72 ++++++++++++++++- 3 files changed, 135 insertions(+), 30 deletions(-) diff --git a/src/braket/aws/aws_quantum_job.py b/src/braket/aws/aws_quantum_job.py index 3120384fe..771611573 100644 --- a/src/braket/aws/aws_quantum_job.py +++ b/src/braket/aws/aws_quantum_job.py @@ -36,6 +36,7 @@ S3DataSourceConfig, StoppingCondition, ) +from braket.jobs.data_persistence import load_job_result from braket.jobs.metrics_data.cwl_insights_metrics_fetcher import CwlInsightsMetricsFetcher # TODO: Have added metric file in metrics folder, but have to decide on the name for keep @@ -43,8 +44,6 @@ from braket.jobs.metrics_data.definitions import MetricStatistic, MetricType from braket.jobs.quantum_job import QuantumJob from braket.jobs.quantum_job_creation import prepare_quantum_job -from braket.jobs.serialization import deserialize_values -from braket.jobs_data import PersistedJobData class AwsQuantumJob(QuantumJob): @@ -482,15 +481,7 @@ def result( @staticmethod def _read_and_deserialize_results(temp_dir: str, job_name: str) -> Dict[str, Any]: - try: - with open(f"{temp_dir}/{job_name}/{AwsQuantumJob.RESULTS_FILENAME}", "r") as f: - persisted_data = PersistedJobData.parse_raw(f.read()) - deserialized_data = deserialize_values( - persisted_data.dataDictionary, persisted_data.dataFormat - ) - return deserialized_data - except FileNotFoundError: - return {} + return load_job_result(Path(temp_dir, job_name, AwsQuantumJob.RESULTS_FILENAME)) def download_result( self, diff --git a/src/braket/jobs/data_persistence.py b/src/braket/jobs/data_persistence.py index 8a04d0a57..aa574d0d9 100644 --- a/src/braket/jobs/data_persistence.py +++ b/src/braket/jobs/data_persistence.py @@ -10,8 +10,8 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. - -from typing import Any, Dict +from pathlib import Path +from typing import Any, Dict, Union from braket.jobs.environment_variables import get_checkpoint_dir, get_job_name, get_results_dir from braket.jobs.serialization import deserialize_values, serialize_values @@ -104,33 +104,83 @@ def load_job_checkpoint(job_name: str, checkpoint_file_suffix: str = "") -> Dict return deserialized_data +def _load_persisted_data(filename: Union[str, Path] = None) -> PersistedJobData: + filename = filename or Path(get_results_dir()) / "results.json" + try: + with open(filename, mode="r") as f: + return PersistedJobData.parse_raw(f.read()) + except FileNotFoundError: + return PersistedJobData( + dataDictionary={}, + dataFormat=PersistedJobDataFormat.PLAINTEXT, + ) + + +def load_job_result(filename: Union[str, Path] = None) -> Dict[str, Any]: + """ + Loads job result of currently running job. + + Args: + filename (Union[str, Path]): Location of job results. Default `results.json` in job + results directory in a job instance or in working directory locally. This file + must be in the format used by `save_job_result`. + + Returns: + Dict[str, Any]: Job result data of current job + """ + persisted_data = _load_persisted_data(filename) + deserialized_data = deserialize_values(persisted_data.dataDictionary, persisted_data.dataFormat) + return deserialized_data + + def save_job_result( - result_data: Dict[str, Any], - data_format: PersistedJobDataFormat = PersistedJobDataFormat.PLAINTEXT, + result_data: Union[Dict[str, Any], Any], + data_format: PersistedJobDataFormat = None, ) -> None: """ Saves the `result_data` to the local output directory that is specified by the container environment variable `AMZN_BRAKET_JOB_RESULTS_DIR`, with the filename 'results.json'. The `result_data` values are serialized to the specified `data_format`. - Note: This function for storing the results is only for use inside the hybrid job container + Note: This function for storing the results is only for use inside the job container as it writes data to directories and references env variables set in the containers. Args: - result_data (Dict[str, Any]): Dict that specifies the result data to be persisted. + result_data (Union[Dict[str, Any], Any]): Dict that specifies the result data to be + persisted. If result data is not a dict, then it will be wrapped as + `{"result": result_data}`. data_format (PersistedJobDataFormat): The data format used to serialize the values. Note that for `PICKLED` data formats, the values are base64 encoded after serialization. Default: PersistedJobDataFormat.PLAINTEXT. - - Raises: - ValueError: If the supplied `result_data` is `None` or empty. """ - if not result_data: - raise ValueError("The result_data argument cannot be empty.") - result_directory = get_results_dir() - result_path = f"{result_directory}/results.json" - with open(result_path, "w") as f: - serialized_data = serialize_values(result_data or {}, data_format) + if not isinstance(result_data, dict): + result_data = {"result": result_data} + + current_persisted_data = _load_persisted_data() + + if current_persisted_data.dataFormat == PersistedJobDataFormat.PICKLED_V4: + # if results are already pickled, maintain pickled format + # if user explicitly specifies plaintext, raise error + if data_format == PersistedJobDataFormat.PLAINTEXT: + raise TypeError( + "Cannot update results object serialized with " + f"{current_persisted_data.dataFormat.value} using data format " + f"{data_format.value}." + ) + + data_format = PersistedJobDataFormat.PICKLED_V4 + + # if not specified or already pickled, default to plaintext + data_format = data_format or PersistedJobDataFormat.PLAINTEXT + + current_results = deserialize_values( + current_persisted_data.dataDictionary, + current_persisted_data.dataFormat, + ) + updated_results = {**current_results, **result_data} + + with open(Path(get_results_dir()) / "results.json", "w") as f: + serialized_data = serialize_values(updated_results or {}, data_format) persisted_data = PersistedJobData(dataDictionary=serialized_data, dataFormat=data_format) f.write(persisted_data.json()) diff --git a/test/unit_tests/braket/jobs/test_data_persistence.py b/test/unit_tests/braket/jobs/test_data_persistence.py index d40d2c203..6a5e27283 100644 --- a/test/unit_tests/braket/jobs/test_data_persistence.py +++ b/test/unit_tests/braket/jobs/test_data_persistence.py @@ -20,7 +20,12 @@ import numpy as np import pytest -from braket.jobs.data_persistence import load_job_checkpoint, save_job_checkpoint, save_job_result +from braket.jobs.data_persistence import ( + load_job_checkpoint, + load_job_result, + save_job_checkpoint, + save_job_result, +) from braket.jobs_data import PersistedJobDataFormat @@ -266,9 +271,68 @@ def test_save_job_result(data_format, result_data, expected_saved_data): assert expected_file.read() == expected_saved_data -@pytest.mark.xfail(raises=ValueError) @pytest.mark.parametrize("result_data", [{}, None]) -def test_save_job_result_raises_error_empty_data(result_data): +def test_save_job_result_does_not_raise_error_empty_data(result_data): with tempfile.TemporaryDirectory() as tmp_dir: - with patch.dict(os.environ, {"AMZN_BRAKET_CHECKPOINT_DIR": tmp_dir}): + with patch.dict(os.environ, {"AMZN_BRAKET_JOB_RESULTS_DIR": tmp_dir}): save_job_result(result_data) + + +@pytest.mark.parametrize( + "first_result_data," + "first_data_format," + "second_result_data," + "second_data_format," + "expected_result_data", + ( + ( + "hello", + PersistedJobDataFormat.PLAINTEXT, + "goodbye", + PersistedJobDataFormat.PLAINTEXT, + {"result": "goodbye"}, + ), + ( + "hello", + PersistedJobDataFormat.PLAINTEXT, + "goodbye", + PersistedJobDataFormat.PICKLED_V4, + {"result": "goodbye"}, + ), + ("hello", PersistedJobDataFormat.PICKLED_V4, "goodbye", None, {"result": "goodbye"}), + ( + # not json serializable + PersistedJobDataFormat, + PersistedJobDataFormat.PICKLED_V4, + {"other_field": "value"}, + None, + {"result": PersistedJobDataFormat, "other_field": "value"}, + ), + ), +) +def test_update_result_data( + first_result_data, + first_data_format, + second_result_data, + second_data_format, + expected_result_data, +): + with tempfile.TemporaryDirectory() as tmp_dir: + with patch.dict(os.environ, {"AMZN_BRAKET_JOB_RESULTS_DIR": tmp_dir}): + save_job_result(first_result_data, first_data_format) + save_job_result(second_result_data, second_data_format) + + assert load_job_result() == expected_result_data + + +def test_update_pickled_results_as_plaintext_error(): + with tempfile.TemporaryDirectory() as tmp_dir: + with patch.dict(os.environ, {"AMZN_BRAKET_JOB_RESULTS_DIR": tmp_dir}): + save_job_result(np.arange(5), PersistedJobDataFormat.PICKLED_V4) + + cannot_convert_pickled_to_plaintext = ( + "Cannot update results object serialized with " + "pickled_v4 using data format plaintext." + ) + with pytest.raises(TypeError, match=cannot_convert_pickled_to_plaintext): + save_job_result("hello", PersistedJobDataFormat.PLAINTEXT) From 3d62becd9e297fabdedc66fe671fa86702728e61 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 4 Oct 2023 10:50:23 -0700 Subject: [PATCH 15/28] fix: revert integ test changes (#722) --- test/integ_tests/job_test_script.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/integ_tests/job_test_script.py b/test/integ_tests/job_test_script.py index 1fd7a364f..7071ffd62 100644 --- a/test/integ_tests/job_test_script.py +++ b/test/integ_tests/job_test_script.py @@ -11,19 +11,19 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. +import json +import os + from braket.aws import AwsDevice from braket.circuits import Circuit -from braket.jobs import ( - get_hyperparameters, - get_job_device_arn, - save_job_checkpoint, - save_job_result, -) +from braket.jobs import save_job_checkpoint, save_job_result from braket.jobs_data import PersistedJobDataFormat def start_here(): - hyperparameters = get_hyperparameters() + hp_file = os.environ["AMZN_BRAKET_HP_FILE"] + with open(hp_file, "r") as f: + hyperparameters = json.load(f) if hyperparameters["test_case"] == "completed": completed_job_script() @@ -40,7 +40,7 @@ def completed_job_script(): print("Test job started!!!!!") # Use the device declared in the Orchestration Script - device = AwsDevice(get_job_device_arn()) + device = AwsDevice(os.environ["AMZN_BRAKET_DEVICE_ARN"]) bell = Circuit().h(0).cnot(0, 1) for count in range(5): From bdea9a1208b73170a03a49d104e623d7f7ab9465 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 4 Oct 2023 18:08:10 +0000 Subject: [PATCH 16/28] prepare release v1.57.0 --- CHANGELOG.md | 11 +++++++++++ src/braket/_sdk/_version.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 238a38e21..4aa6cf9f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v1.57.0 (2023-10-04) + +### Features + + * wrap non-dict results and update results on subsequent calls + * job helper functions + +### Bug Fixes and Other Changes + + * revert integ test changes + ## v1.56.2 (2023-10-03) ### Bug Fixes and Other Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index b01e8f75b..3538fc585 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.56.3.dev0" +__version__ = "1.57.0" From abfda05c99b60945a44f396357ef86306ffd0149 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 4 Oct 2023 18:08:10 +0000 Subject: [PATCH 17/28] update development version to v1.57.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 3538fc585..e32e384df 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.57.0" +__version__ = "1.57.1.dev0" From 7977b948702db7549878ebdca071cfe49a6ea2c6 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 4 Oct 2023 14:34:23 -0700 Subject: [PATCH 18/28] test: add job helpers to integ test (#724) This reverts commit 3d62becd9e297fabdedc66fe671fa86702728e61. --- test/integ_tests/job_test_script.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/integ_tests/job_test_script.py b/test/integ_tests/job_test_script.py index 7071ffd62..1fd7a364f 100644 --- a/test/integ_tests/job_test_script.py +++ b/test/integ_tests/job_test_script.py @@ -11,19 +11,19 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -import json -import os - from braket.aws import AwsDevice from braket.circuits import Circuit -from braket.jobs import save_job_checkpoint, save_job_result +from braket.jobs import ( + get_hyperparameters, + get_job_device_arn, + save_job_checkpoint, + save_job_result, +) from braket.jobs_data import PersistedJobDataFormat def start_here(): - hp_file = os.environ["AMZN_BRAKET_HP_FILE"] - with open(hp_file, "r") as f: - hyperparameters = json.load(f) + hyperparameters = get_hyperparameters() if hyperparameters["test_case"] == "completed": completed_job_script() @@ -40,7 +40,7 @@ def completed_job_script(): print("Test job started!!!!!") # Use the device declared in the Orchestration Script - device = AwsDevice(os.environ["AMZN_BRAKET_DEVICE_ARN"]) + device = AwsDevice(get_job_device_arn()) bell = Circuit().h(0).cnot(0, 1) for count in range(5): From ba6bb52ea0b22087efb2bf4cf2c3526eb2c07df0 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Wed, 4 Oct 2023 15:36:35 -0700 Subject: [PATCH 19/28] docs: fix helper docstring (#725) --- src/braket/jobs/environment_variables.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/braket/jobs/environment_variables.py b/src/braket/jobs/environment_variables.py index 489b1f18e..e298304ca 100644 --- a/src/braket/jobs/environment_variables.py +++ b/src/braket/jobs/environment_variables.py @@ -53,6 +53,7 @@ def get_results_dir() -> str: def get_checkpoint_dir() -> str: """ Get the job checkpoint directory. + Returns: str: The checkpoint directory, defaulting to current working directory. """ @@ -61,9 +62,10 @@ def get_checkpoint_dir() -> str: def get_hyperparameters() -> Dict[str, str]: """ - Get the job checkpoint directory. + Get the job hyperparameters as strings. + Returns: - str: The checkpoint directory, defaulting to current working directory. + Dict[str, str]: The hyperparameters of the job. """ if "AMZN_BRAKET_HP_FILE" in os.environ: with open(os.getenv("AMZN_BRAKET_HP_FILE"), "r") as f: From 52059c5bcae5777b3246e2836d5cb86b7cbbb227 Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 5 Oct 2023 16:15:19 +0000 Subject: [PATCH 20/28] prepare release v1.57.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 4aa6cf9f9..3e7fd4e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.57.1 (2023-10-05) + +### Bug Fixes and Other Changes + + * docs: fix helper docstring + ## v1.57.0 (2023-10-04) ### Features diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index e32e384df..e4a9c4c54 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.57.1.dev0" +__version__ = "1.57.1" From 6eee1aac18625a6f3e32e6bb75b60500f357c9fa Mon Sep 17 00:00:00 2001 From: ci Date: Thu, 5 Oct 2023 16:15:19 +0000 Subject: [PATCH 21/28] update development version to v1.57.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 e4a9c4c54..a16206e2c 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.57.1" +__version__ = "1.57.2.dev0" From 4dd45a9388385f30b8fae5f630cb9bb70a42c8c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:32:42 -0700 Subject: [PATCH 22/28] infra: bump actions/setup-python from 4.7.0 to 4.7.1 (#728) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.0 to 4.7.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/61a6322f88396a6271a6ee3565807d608ecaddd1...65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... --- .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 631b90360..0e0b99276 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: '3.x' - name: Install dependencies diff --git a/.github/workflows/dependent-tests.yml b/.github/workflows/dependent-tests.yml index 10752e27e..e70ea3137 100644 --- a/.github/workflows/dependent-tests.yml +++ b/.github/workflows/dependent-tests.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 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 fbd9a8942..e4cbed2de 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: '3.x' - name: Install wheel diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1e316a8cc..7593eb916 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/twine-check.yml b/.github/workflows/twine-check.yml index f73925f8a..8832a8a86 100644 --- a/.github/workflows/twine-check.yml +++ b/.github/workflows/twine-check.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: '3.x' - name: Install wheel From 02316ffd4bab6270e3cc97c5b2cb8cbf39e8dbe1 Mon Sep 17 00:00:00 2001 From: Abe Coull <85974725+math411@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:00:58 -0700 Subject: [PATCH 23/28] infra: drop support for Python 3.8 (#719) --- .github/workflows/dependent-tests.yml | 2 +- .github/workflows/python-package.yml | 2 +- .readthedocs.yml | 2 +- README.md | 4 ++-- setup.py | 3 +-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dependent-tests.yml b/.github/workflows/dependent-tests.yml index e70ea3137..320195c9a 100644 --- a/.github/workflows/dependent-tests.yml +++ b/.github/workflows/dependent-tests.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11"] dependent: - amazon-braket-pennylane-plugin-python diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 7593eb916..c27a0542a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11"] steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 diff --git a/.readthedocs.yml b/.readthedocs.yml index 59d0f1631..e824a6afc 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -17,7 +17,7 @@ formats: build: os: ubuntu-22.04 tools: - python: "3.8" + python: "3.9" # Optionally set the version of Python and requirements required to build your docs python: diff --git a/README.md b/README.md index d85337136..f27fcc6bf 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ The Amazon Braket Python SDK is an open source library that provides a framework ## Prerequisites Before you begin working with the Amazon Braket SDK, make sure that you've installed or configured the following prerequisites. -### Python 3.8 or greater -Download and install Python 3.8 or greater from [Python.org](https://www.python.org/downloads/). +### Python 3.9 or greater +Download and install Python 3.9 or greater from [Python.org](https://www.python.org/downloads/). ### Git Install Git from https://git-scm.com/downloads. Installation instructions are provided on the download page. diff --git a/setup.py b/setup.py index 4dff452cf..428cbb58b 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ name="amazon-braket-sdk", version=version, license="Apache License 2.0", - python_requires=">= 3.8.2", + python_requires=">= 3.9", packages=find_namespace_packages(where="src", exclude=("test",)), package_dir={"": "src"}, install_requires=[ @@ -76,7 +76,6 @@ "Natural Language :: English", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", From cbbd40eda1dd70a35929abddb421ca5336151240 Mon Sep 17 00:00:00 2001 From: Cody Wang Date: Tue, 10 Oct 2023 18:46:51 -0700 Subject: [PATCH 24/28] Use builtins for type hints (#734) --- examples/job.py | 2 - .../ahs/analog_hamiltonian_simulation.py | 7 +- src/braket/ahs/atom_arrangement.py | 13 +- src/braket/ahs/driving_field.py | 14 +-- src/braket/ahs/hamiltonian.py | 8 +- src/braket/ahs/pattern.py | 9 +- src/braket/ahs/shifting_field.py | 12 +- src/braket/aws/aws_device.py | 74 ++++++------ src/braket/aws/aws_quantum_task.py | 72 ++++++------ src/braket/aws/aws_quantum_task_batch.py | 48 ++++---- src/braket/aws/aws_session.py | 58 ++++----- src/braket/aws/queue_information.py | 6 +- src/braket/circuits/angled_gate.py | 27 +++-- src/braket/circuits/ascii_circuit_diagram.py | 26 ++-- src/braket/circuits/basis_state.py | 4 +- src/braket/circuits/braket_program_context.py | 26 ++-- src/braket/circuits/circuit.py | 111 +++++++++--------- src/braket/circuits/circuit_diagram.py | 1 + src/braket/circuits/compiler_directive.py | 7 +- src/braket/circuits/gate.py | 15 +-- src/braket/circuits/gate_calibrations.py | 26 ++-- src/braket/circuits/gates.py | 55 ++++----- src/braket/circuits/instruction.py | 18 +-- src/braket/circuits/moments.py | 22 +--- src/braket/circuits/noise.py | 37 +++--- src/braket/circuits/noise_helpers.py | 31 ++--- .../circuit_instruction_criteria.py | 6 +- src/braket/circuits/noise_model/criteria.py | 11 +- .../noise_model/criteria_input_parsing.py | 11 +- .../circuits/noise_model/gate_criteria.py | 7 +- .../circuits/noise_model/noise_model.py | 42 +++---- .../noise_model/observable_criteria.py | 7 +- .../qubit_initialization_criteria.py | 7 +- .../noise_model/unitary_gate_criteria.py | 7 +- src/braket/circuits/noises.py | 17 +-- src/braket/circuits/observable.py | 15 +-- src/braket/circuits/observables.py | 70 +++++------ src/braket/circuits/quantum_operator.py | 7 +- .../circuits/quantum_operator_helpers.py | 2 +- src/braket/circuits/result_type.py | 32 ++--- src/braket/circuits/result_types.py | 30 ++--- src/braket/circuits/unitary_calculation.py | 2 +- src/braket/devices/device.py | 14 +-- src/braket/devices/local_simulator.py | 24 ++-- src/braket/error_mitigation/debias.py | 4 +- .../error_mitigation/error_mitigation.py | 6 +- src/braket/jobs/data_persistence.py | 2 +- src/braket/parametric/free_parameter.py | 6 +- .../parametric/free_parameter_expression.py | 6 +- src/braket/parametric/parameterizable.py | 6 +- src/braket/pulse/ast/approximation_parser.py | 22 ++-- src/braket/pulse/ast/free_parameters.py | 5 +- src/braket/pulse/ast/qasm_parser.py | 1 + src/braket/pulse/ast/qasm_transformer.py | 1 + src/braket/pulse/frame.py | 6 +- src/braket/pulse/port.py | 6 +- src/braket/pulse/pulse_sequence.py | 28 ++--- src/braket/pulse/pulse_sequence_trace.py | 7 +- src/braket/pulse/waveforms.py | 26 ++-- .../quantum_information/pauli_string.py | 14 +-- src/braket/registers/qubit_set.py | 7 +- ...iltonian_simulation_quantum_task_result.py | 9 +- .../tasks/gate_model_quantum_task_result.py | 57 ++++----- src/braket/tasks/local_quantum_task_batch.py | 6 +- src/braket/tasks/quantum_task.py | 6 +- src/braket/tasks/quantum_task_batch.py | 6 +- src/braket/timings/time_series.py | 29 ++--- src/braket/tracking/pricing.py | 5 +- src/braket/tracking/tracker.py | 10 +- 69 files changed, 662 insertions(+), 659 deletions(-) diff --git a/examples/job.py b/examples/job.py index f5e330d1a..87b06bf49 100644 --- a/examples/job.py +++ b/examples/job.py @@ -11,8 +11,6 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -import os - from braket.aws import AwsDevice, AwsQuantumJob from braket.circuits import Circuit from braket.devices import Devices diff --git a/src/braket/ahs/analog_hamiltonian_simulation.py b/src/braket/ahs/analog_hamiltonian_simulation.py index 80c4f2f59..b02091d4f 100644 --- a/src/braket/ahs/analog_hamiltonian_simulation.py +++ b/src/braket/ahs/analog_hamiltonian_simulation.py @@ -15,7 +15,6 @@ from collections import defaultdict from functools import singledispatch -from typing import Tuple import braket.ir.ahs as ir from braket.ahs.atom_arrangement import AtomArrangement, SiteType @@ -113,12 +112,12 @@ def discretize(self, device) -> AnalogHamiltonianSimulation: # noqa @singledispatch def _get_term_ir( term: Hamiltonian, -) -> Tuple[str, dict]: +) -> tuple[str, dict]: raise TypeError(f"Unable to convert Hamiltonian term type {type(term)}.") @_get_term_ir.register -def _(term: ShiftingField) -> Tuple[str, ir.ShiftingField]: +def _(term: ShiftingField) -> tuple[str, ir.ShiftingField]: return AnalogHamiltonianSimulation.SHIFTING_FIELDS_PROPERTY, ir.ShiftingField( magnitude=ir.PhysicalField( time_series=ir.TimeSeries( @@ -131,7 +130,7 @@ def _(term: ShiftingField) -> Tuple[str, ir.ShiftingField]: @_get_term_ir.register -def _(term: DrivingField) -> Tuple[str, ir.DrivingField]: +def _(term: DrivingField) -> tuple[str, ir.DrivingField]: return AnalogHamiltonianSimulation.DRIVING_FIELDS_PROPERTY, ir.DrivingField( amplitude=ir.PhysicalField( time_series=ir.TimeSeries( diff --git a/src/braket/ahs/atom_arrangement.py b/src/braket/ahs/atom_arrangement.py index 70fc64b0d..bb7088347 100644 --- a/src/braket/ahs/atom_arrangement.py +++ b/src/braket/ahs/atom_arrangement.py @@ -13,11 +13,12 @@ from __future__ import annotations +from collections.abc import Iterator from dataclasses import dataclass from decimal import Decimal from enum import Enum from numbers import Number -from typing import Iterator, List, Tuple, Union +from typing import Union import numpy as np @@ -33,7 +34,7 @@ class SiteType(Enum): class AtomArrangementItem: """Represents an item (coordinate and metadata) in an atom arrangement.""" - coordinate: Tuple[Number, Number] + coordinate: tuple[Number, Number] site_type: SiteType def _validate_coordinate(self) -> None: @@ -62,13 +63,13 @@ def __init__(self): def add( self, - coordinate: Union[Tuple[Number, Number], np.ndarray], + coordinate: Union[tuple[Number, Number], np.ndarray], site_type: SiteType = SiteType.FILLED, ) -> AtomArrangement: """Add a coordinate to the atom arrangement. Args: - coordinate (Union[Tuple[Number, Number], ndarray]): The coordinate of the + coordinate (Union[tuple[Number, Number], ndarray]): The coordinate of the atom (in meters). The coordinates can be a numpy array of shape (2,) or a tuple of int, float, Decimal site_type (SiteType): The type of site. Optional. Default is FILLED. @@ -78,14 +79,14 @@ def add( self._sites.append(AtomArrangementItem(tuple(coordinate), site_type)) return self - def coordinate_list(self, coordinate_index: Number) -> List[Number]: + def coordinate_list(self, coordinate_index: Number) -> list[Number]: """Returns all the coordinates at the given index. Args: coordinate_index (Number): The index to get for each coordinate. Returns: - List[Number]:The list of coordinates at the given index. + list[Number]:The list of coordinates at the given index. Example: To get a list of all x-coordinates: coordinate_list(0) diff --git a/src/braket/ahs/driving_field.py b/src/braket/ahs/driving_field.py index 45914063a..02c8bd276 100644 --- a/src/braket/ahs/driving_field.py +++ b/src/braket/ahs/driving_field.py @@ -13,7 +13,7 @@ from __future__ import annotations -from typing import List, Union +from typing import Union from braket.ahs.discretization_types import DiscretizationProperties from braket.ahs.field import Field @@ -65,7 +65,7 @@ def __init__( self._detuning = detuning if isinstance(detuning, Field) else Field(detuning) @property - def terms(self) -> List[Hamiltonian]: + def terms(self) -> list[Hamiltonian]: return [self] @property @@ -141,7 +141,7 @@ def discretize(self, properties: DiscretizationProperties) -> DrivingField: @staticmethod def from_lists( - times: List[float], amplitudes: List[float], detunings: List[float], phases: List[float] + times: list[float], amplitudes: list[float], detunings: list[float], phases: list[float] ) -> DrivingField: """ Builds DrivingField Hamiltonian from lists defining time evolution @@ -149,10 +149,10 @@ def from_lists( The values of the parameters at each time points are global for all atoms. Args: - times (List[float]): The time points of the driving field - amplitudes (List[float]): The values of the amplitude - detunings (List[float]): The values of the detuning - phases (List[float]): The values of the phase + times (list[float]): The time points of the driving field + amplitudes (list[float]): The values of the amplitude + detunings (list[float]): The values of the detuning + phases (list[float]): The values of the phase Returns: DrivingField: DrivingField Hamiltonian. diff --git a/src/braket/ahs/hamiltonian.py b/src/braket/ahs/hamiltonian.py index 83b80e69c..548befc58 100644 --- a/src/braket/ahs/hamiltonian.py +++ b/src/braket/ahs/hamiltonian.py @@ -13,13 +13,13 @@ from __future__ import annotations -from typing import List, Optional +from typing import Optional from braket.ahs.discretization_types import DiscretizationProperties class Hamiltonian: - def __init__(self, terms: Optional[List[Hamiltonian]] = None): + def __init__(self, terms: Optional[list[Hamiltonian]] = None): r"""A Hamiltonian representing a system to be simulated. A Hamiltonian :math:`H` may be expressed as a sum of multiple terms @@ -30,8 +30,8 @@ def __init__(self, terms: Optional[List[Hamiltonian]] = None): self._terms = terms or [] @property - def terms(self) -> List[Hamiltonian]: - """List[Hamiltonian]: The list of terms in this Hamiltonian.""" + def terms(self) -> list[Hamiltonian]: + """list[Hamiltonian]: The list of terms in this Hamiltonian.""" return self._terms def discretize(self, properties: DiscretizationProperties) -> Hamiltonian: diff --git a/src/braket/ahs/pattern.py b/src/braket/ahs/pattern.py index ebd1eafc1..17e40a36f 100644 --- a/src/braket/ahs/pattern.py +++ b/src/braket/ahs/pattern.py @@ -15,22 +15,21 @@ from decimal import Decimal from numbers import Number -from typing import List class Pattern: - def __init__(self, series: List[Number]): + def __init__(self, series: list[Number]): """Represents the spatial dependence of a Field. Args: - series (List[Number]): A series of numbers representing the the local + series (list[Number]): A series of numbers representing the the local pattern of real numbers. """ self._series = series @property - def series(self) -> List[Number]: - """List[Number]: A series of numbers representing the local + def series(self) -> list[Number]: + """list[Number]: A series of numbers representing the local pattern of real numbers.""" return self._series diff --git a/src/braket/ahs/shifting_field.py b/src/braket/ahs/shifting_field.py index bd3f59da9..846d7ad2e 100644 --- a/src/braket/ahs/shifting_field.py +++ b/src/braket/ahs/shifting_field.py @@ -13,8 +13,6 @@ from __future__ import annotations -from typing import List - from braket.ahs.discretization_types import DiscretizationProperties from braket.ahs.field import Field from braket.ahs.hamiltonian import Hamiltonian @@ -51,7 +49,7 @@ def __init__(self, magnitude: Field) -> None: self._magnitude = magnitude @property - def terms(self) -> List[Hamiltonian]: + def terms(self) -> list[Hamiltonian]: return [self] @property @@ -62,13 +60,13 @@ def magnitude(self) -> Field: return self._magnitude @staticmethod - def from_lists(times: List[float], values: List[float], pattern: List[float]) -> ShiftingField: + def from_lists(times: list[float], values: list[float], pattern: list[float]) -> ShiftingField: """Get the shifting field from a set of time points, values and pattern Args: - times (List[float]): The time points of the shifting field - values (List[float]): The values of the shifting field - pattern (List[float]): The pattern of the shifting field + times (list[float]): The time points of the shifting field + values (list[float]): The values of the shifting field + pattern (list[float]): The pattern of the shifting field Returns: ShiftingField: The shifting field obtained diff --git a/src/braket/aws/aws_device.py b/src/braket/aws/aws_device.py index d495dbe3f..c9a208411 100644 --- a/src/braket/aws/aws_device.py +++ b/src/braket/aws/aws_device.py @@ -20,7 +20,7 @@ import warnings from datetime import datetime from enum import Enum -from typing import Dict, List, Optional, Tuple, Union +from typing import Optional, Union from botocore.errorfactory import ClientError from networkx import DiGraph, complete_graph, from_edgelist @@ -119,8 +119,8 @@ def run( shots: Optional[int] = None, poll_timeout_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_TIMEOUT, poll_interval_seconds: Optional[float] = None, - inputs: Optional[Dict[str, float]] = None, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] = None, + inputs: Optional[dict[str, float]] = None, + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] = None, *aws_quantum_task_args, **aws_quantum_task_kwargs, ) -> AwsQuantumTask: @@ -142,11 +142,11 @@ def run( poll_interval_seconds (Optional[float]): The polling interval for `AwsQuantumTask.result()`, in seconds. Defaults to the ``getTaskPollIntervalMillis`` value specified in ``self.properties.service`` (divided by 1000) if provided, otherwise 1 second. - inputs (Optional[Dict[str, float]]): Inputs to be passed along with the + inputs (Optional[dict[str, float]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. - gate_definitions (Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]]): A - `Dict[Tuple[Gate, QubitSet], PulseSequence]]` for a user defined gate calibration. + gate_definitions (Optional[dict[tuple[Gate, QubitSet], PulseSequence]]): A + `dict[tuple[Gate, QubitSet], PulseSequence]]` for a user defined gate calibration. The calibration is defined for a particular `Gate` on a particular `QubitSet` and is represented by a `PulseSequence`. Default: None. @@ -214,7 +214,7 @@ def run_batch( PulseSequence, AnalogHamiltonianSimulation, ], - List[ + list[ Union[ Circuit, Problem, @@ -231,15 +231,15 @@ def run_batch( max_connections: int = AwsQuantumTaskBatch.MAX_CONNECTIONS_DEFAULT, poll_timeout_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_TIMEOUT, poll_interval_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_INTERVAL, - inputs: Optional[Union[Dict[str, float], List[Dict[str, float]]]] = None, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] = None, + inputs: Optional[Union[dict[str, float], list[dict[str, float]]]] = None, + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] = None, *aws_quantum_task_args, **aws_quantum_task_kwargs, ) -> AwsQuantumTaskBatch: """Executes a batch of quantum tasks in parallel Args: - task_specifications (Union[Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, PulseSequence, AnalogHamiltonianSimulation], List[Union[ Circuit, Problem, OpenQasmProgram, BlackbirdProgram, PulseSequence, AnalogHamiltonianSimulation]]]): # noqa + task_specifications (Union[Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, PulseSequence, AnalogHamiltonianSimulation], list[Union[ Circuit, Problem, OpenQasmProgram, BlackbirdProgram, PulseSequence, AnalogHamiltonianSimulation]]]): # noqa Single instance or list of circuits, annealing problems, pulse sequences, or photonics program to run on device. s3_destination_folder (Optional[S3DestinationFolder]): The S3 location to @@ -257,11 +257,11 @@ def run_batch( poll_interval_seconds (float): The polling interval for `AwsQuantumTask.result()`, in seconds. Defaults to the ``getTaskPollIntervalMillis`` value specified in ``self.properties.service`` (divided by 1000) if provided, otherwise 1 second. - inputs (Optional[Union[Dict[str, float], List[Dict[str, float]]]]): Inputs to be + inputs (Optional[Union[dict[str, float], list[dict[str, float]]]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. - gate_definitions (Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]]): A - `Dict[Tuple[Gate, QubitSet], PulseSequence]]` for a user defined gate calibration. + gate_definitions (Optional[dict[tuple[Gate, QubitSet], PulseSequence]]): A + `dict[tuple[Gate, QubitSet], PulseSequence]]` for a user defined gate calibration. The calibration is defined for a particular `Gate` on a particular `QubitSet` and is represented by a `PulseSequence`. Default: None. @@ -531,29 +531,29 @@ def __eq__(self, other): return NotImplemented @property - def frames(self) -> Dict[str, Frame]: - """Returns a Dict mapping frame ids to the frame objects for predefined frames + def frames(self) -> dict[str, Frame]: + """Returns a dict mapping frame ids to the frame objects for predefined frames for this device.""" self._update_pulse_properties() return self._frames or dict() @property - def ports(self) -> Dict[str, Port]: - """Returns a Dict mapping port ids to the port objects for predefined ports + def ports(self) -> dict[str, Port]: + """Returns a dict mapping port ids to the port objects for predefined ports for this device.""" self._update_pulse_properties() return self._ports or dict() @staticmethod def get_devices( - arns: Optional[List[str]] = None, - names: Optional[List[str]] = None, - types: Optional[List[AwsDeviceType]] = None, - statuses: Optional[List[str]] = None, - provider_names: Optional[List[str]] = None, + arns: Optional[list[str]] = None, + names: Optional[list[str]] = None, + types: Optional[list[AwsDeviceType]] = None, + statuses: Optional[list[str]] = None, + provider_names: Optional[list[str]] = None, order_by: str = "name", aws_session: Optional[AwsSession] = None, - ) -> List[AwsDevice]: + ) -> list[AwsDevice]: """ Get devices based on filters and desired ordering. The result is the AND of all the filters `arns`, `names`, `types`, `statuses`, `provider_names`. @@ -564,20 +564,20 @@ def get_devices( >>> AwsDevice.get_devices(types=['SIMULATOR']) Args: - arns (Optional[List[str]]): device ARN list, default is `None` - names (Optional[List[str]]): device name list, default is `None` - types (Optional[List[AwsDeviceType]]): device type list, default is `None` + arns (Optional[list[str]]): device ARN list, default is `None` + names (Optional[list[str]]): device name list, default is `None` + types (Optional[list[AwsDeviceType]]): device type list, default is `None` QPUs will be searched for all regions and simulators will only be searched for the region of the current session. - statuses (Optional[List[str]]): device status list, default is `None` - provider_names (Optional[List[str]]): provider name list, default is `None` + statuses (Optional[list[str]]): device status list, default is `None` + provider_names (Optional[list[str]]): provider name list, default is `None` order_by (str): field to order result by, default is `name`. Accepted values are ['arn', 'name', 'type', 'provider_name', 'status'] aws_session (Optional[AwsSession]): An AWS session object. Default is `None`. Returns: - List[AwsDevice]: list of AWS devices + list[AwsDevice]: list of AWS devices """ if order_by not in AwsDevice._GET_DEVICES_ORDER_BY_KEYS: @@ -759,7 +759,7 @@ def refresh_gate_calibrations(self) -> Optional[GateCalibrations]: else: return None - def _parse_waveforms(self, waveforms_json: Dict) -> Dict: + def _parse_waveforms(self, waveforms_json: dict) -> dict: waveforms = dict() for waveform in waveforms_json: parsed_waveform = _parse_waveform_from_calibration_schema(waveforms_json[waveform]) @@ -767,24 +767,24 @@ def _parse_waveforms(self, waveforms_json: Dict) -> Dict: return waveforms def _parse_pulse_sequence( - self, calibration: Dict, waveforms: Dict[ArbitraryWaveform] + self, calibration: dict, waveforms: dict[ArbitraryWaveform] ) -> PulseSequence: return PulseSequence._parse_from_calibration_schema(calibration, waveforms, self.frames) def _parse_calibration_json( - self, calibration_data: Dict - ) -> Dict[Tuple[Gate, QubitSet], PulseSequence]: + self, calibration_data: dict + ) -> dict[tuple[Gate, QubitSet], PulseSequence]: """ Takes the json string from the device calibration URL and returns a structured dictionary of - corresponding `Dict[Tuple[Gate, QubitSet], PulseSequence]` to represent the calibration data. + corresponding `dict[tuple[Gate, QubitSet], PulseSequence]` to represent the calibration data. Args: - calibration_data (Dict): The data to be parsed. Based on + calibration_data (dict): The data to be parsed. Based on https://github.com/aws/amazon-braket-schemas-python/blob/main/src/braket/device_schema/pulse/native_gate_calibrations_v1.py. Returns: - Dict[Tuple[Gate, QubitSet], PulseSequence]: The - structured data based on a mapping of `Tuple[Gate, Qubit]` to its calibration repesented as a + dict[tuple[Gate, QubitSet], PulseSequence]: The + structured data based on a mapping of `tuple[Gate, Qubit]` to its calibration repesented as a `PulseSequence`. """ # noqa: E501 diff --git a/src/braket/aws/aws_quantum_task.py b/src/braket/aws/aws_quantum_task.py index 57fecb26c..346816e71 100644 --- a/src/braket/aws/aws_quantum_task.py +++ b/src/braket/aws/aws_quantum_task.py @@ -17,7 +17,7 @@ import time from functools import singledispatch from logging import Logger, getLogger -from typing import Any, Dict, Optional, Tuple, Union +from typing import Any, Optional, Union import boto3 @@ -100,11 +100,11 @@ def create( ], s3_destination_folder: AwsSession.S3DestinationFolder, shots: int, - device_parameters: Dict[str, Any] = None, + device_parameters: dict[str, Any] = None, disable_qubit_rewiring: bool = False, - tags: Dict[str, str] = None, - inputs: Dict[str, float] = None, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] = None, + tags: dict[str, str] = None, + inputs: dict[str, float] = None, + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] = None, *args, **kwargs, ) -> AwsQuantumTask: @@ -129,7 +129,7 @@ def create( `shots=0` is only available on simulators and means that the simulator will compute the exact results based on the quantum task specification. - device_parameters (Dict[str, Any]): Additional parameters to send to the device. + device_parameters (dict[str, Any]): Additional parameters to send to the device. disable_qubit_rewiring (bool): Whether to run the circuit with the exact qubits chosen, without any rewiring downstream, if this is supported by the device. @@ -137,15 +137,15 @@ def create( If ``True``, no qubit rewiring is allowed; if ``False``, qubit rewiring is allowed. Default: False - tags (Dict[str, str]): Tags, which are Key-Value pairs to add to this quantum task. + tags (dict[str, str]): Tags, which are Key-Value pairs to add to this quantum task. An example would be: `{"state": "washington"}` - inputs (Dict[str, float]): Inputs to be passed along with the + inputs (dict[str, float]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. - gate_definitions (Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]]): + gate_definitions (Optional[dict[tuple[Gate, QubitSet], PulseSequence]]): A `Dict` for user defined gate calibration. The calibration is defined for for a particular `Gate` on a particular `QubitSet` and is represented by a `PulseSequence`. @@ -241,7 +241,7 @@ def __init__( self._logger = logger - self._metadata: Dict[str, Any] = {} + self._metadata: dict[str, Any] = {} self._result: Union[ GateModelQuantumTaskResult, AnnealingQuantumTaskResult, PhotonicModelQuantumTaskResult ] = None @@ -278,7 +278,7 @@ def cancel(self) -> None: self._cancel_future() self._aws_session.cancel_quantum_task(self._arn) - def metadata(self, use_cached_value: bool = False) -> Dict[str, Any]: + def metadata(self, use_cached_value: bool = False) -> dict[str, Any]: """ Get quantum task metadata defined in Amazon Braket. @@ -288,7 +288,7 @@ def metadata(self, use_cached_value: bool = False) -> Dict[str, Any]: `GetQuantumTask` will be called to retrieve the metadata. If `False`, always calls `GetQuantumTask`, which also updates the cached value. Default: `False`. Returns: - Dict[str, Any]: The response from the Amazon Braket `GetQuantumTask` operation. + dict[str, Any]: The response from the Amazon Braket `GetQuantumTask` operation. If `use_cached_value` is `True`, Amazon Braket is not called and the most recently retrieved value is used, unless `GetQuantumTask` was never called, in which case it wil still be called to populate the metadata for the first time. @@ -514,12 +514,12 @@ def __hash__(self) -> int: def _create_internal( task_specification: Union[Circuit, Problem, BlackbirdProgram], aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, device_parameters: Union[dict, BraketSchemaBase], disable_qubit_rewiring: bool, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -530,12 +530,12 @@ def _create_internal( def _( pulse_sequence: PulseSequence, aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, _device_parameters: Union[dict, BraketSchemaBase], # Not currently used for OpenQasmProgram _disable_qubit_rewiring: bool, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -548,12 +548,12 @@ def _( def _( openqasm_program: OpenQASMProgram, aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, device_parameters: Union[dict, BraketSchemaBase], _disable_qubit_rewiring: bool, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -587,12 +587,12 @@ def _( def _( blackbird_program: BlackbirdProgram, aws_session: AwsSession, - create_task_kwargs: Dict[str, any], + create_task_kwargs: dict[str, any], device_arn: str, _device_parameters: Union[dict, BraketSchemaBase], _disable_qubit_rewiring: bool, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -605,12 +605,12 @@ def _( def _( circuit: Circuit, aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, device_parameters: Union[dict, BraketSchemaBase], disable_qubit_rewiring: bool, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -668,7 +668,7 @@ def _( def _( problem: Problem, aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, device_parameters: Union[ dict, @@ -677,8 +677,8 @@ def _( Dwave2000QDeviceParameters, ], _, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -698,12 +698,12 @@ def _( def _( analog_hamiltonian_simulation: AnalogHamiltonianSimulation, aws_session: AwsSession, - create_task_kwargs: Dict[str, Any], + create_task_kwargs: dict[str, Any], device_arn: str, device_parameters: dict, _, - inputs: Dict[str, float], - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + inputs: dict[str, float], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], *args, **kwargs, ) -> AwsQuantumTask: @@ -732,12 +732,12 @@ def _circuit_device_params_from_dict( def _create_annealing_device_params( - device_params: Dict[str, Any], device_arn: str + device_params: dict[str, Any], device_arn: str ) -> Union[DwaveAdvantageDeviceParameters, Dwave2000QDeviceParameters]: """Gets Annealing Device Parameters. Args: - device_params (Dict[str, Any]): Additional parameters for the device. + device_params (dict[str, Any]): Additional parameters for the device. device_arn (str): The ARN of the quantum device. Returns: @@ -774,7 +774,7 @@ def _create_annealing_device_params( def _create_common_params( device_arn: str, s3_destination_folder: AwsSession.S3DestinationFolder, shots: int -) -> Dict[str, Any]: +) -> dict[str, Any]: return { "deviceArn": device_arn, "outputS3Bucket": s3_destination_folder[0], diff --git a/src/braket/aws/aws_quantum_task_batch.py b/src/braket/aws/aws_quantum_task_batch.py index caa48b39c..d29533a95 100644 --- a/src/braket/aws/aws_quantum_task_batch.py +++ b/src/braket/aws/aws_quantum_task_batch.py @@ -16,7 +16,7 @@ import time from concurrent.futures.thread import ThreadPoolExecutor from itertools import repeat -from typing import Dict, List, Set, Tuple, Union +from typing import Union from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation from braket.annealing import Problem @@ -48,7 +48,7 @@ def __init__( device_arn: str, task_specifications: Union[ Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation], - List[ + list[ Union[ Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation ] @@ -60,7 +60,7 @@ def __init__( max_workers: int = MAX_CONNECTIONS_DEFAULT, poll_timeout_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_TIMEOUT, poll_interval_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_INTERVAL, - inputs: Union[Dict[str, float], List[Dict[str, float]]] = None, + inputs: Union[dict[str, float], list[dict[str, float]]] = None, *aws_quantum_task_args, **aws_quantum_task_kwargs, ): @@ -69,7 +69,7 @@ def __init__( Args: aws_session (AwsSession): AwsSession to connect to AWS with. device_arn (str): The ARN of the quantum device. - task_specifications (Union[Union[Circuit,Problem,OpenQasmProgram,BlackbirdProgram,AnalogHamiltonianSimulation],List[Union[Circuit,Problem,OpenQasmProgram,BlackbirdProgram,AnalogHamiltonianSimulation]]]): # noqa + task_specifications (Union[Union[Circuit,Problem,OpenQasmProgram,BlackbirdProgram,AnalogHamiltonianSimulation],list[Union[Circuit,Problem,OpenQasmProgram,BlackbirdProgram,AnalogHamiltonianSimulation]]]): # noqa Single instance or list of circuits, annealing problems, pulse sequences, or photonics program as specification of quantum task to run on device. @@ -88,7 +88,7 @@ def __init__( in seconds. Default: 5 days. poll_interval_seconds (float): The polling interval for results in seconds. Default: 1 second. - inputs (Union[Dict[str, float], List[Dict[str, float]]]): Inputs to be passed + inputs (Union[dict[str, float], list[dict[str, float]]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. """ @@ -127,17 +127,17 @@ def __init__( def _tasks_and_inputs( task_specifications: Union[ Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation], - List[ + list[ Union[ Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation ] ], ], - inputs: Union[Dict[str, float], List[Dict[str, float]]] = None, - ) -> List[ - Tuple[ + inputs: Union[dict[str, float], list[dict[str, float]]] = None, + ) -> list[ + tuple[ Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation], - Dict[str, float], + dict[str, float], ] ]: inputs = inputs or {} @@ -184,7 +184,7 @@ def _execute( device_arn: str, task_specifications: Union[ Union[Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation], - List[ + list[ Union[ Circuit, Problem, OpenQasmProgram, BlackbirdProgram, AnalogHamiltonianSimulation ] @@ -196,10 +196,10 @@ def _execute( max_workers: int = MAX_CONNECTIONS_DEFAULT, poll_timeout_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_TIMEOUT, poll_interval_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_INTERVAL, - inputs: Union[Dict[str, float], List[Dict[str, float]]] = None, + inputs: Union[dict[str, float], list[dict[str, float]]] = None, *args, **kwargs, - ) -> List[AwsQuantumTask]: + ) -> list[AwsQuantumTask]: tasks_and_inputs = AwsQuantumTaskBatch._tasks_and_inputs(task_specifications, inputs) max_threads = min(max_parallel, max_workers) remaining = [0 for _ in tasks_and_inputs] @@ -238,7 +238,7 @@ def _execute( @staticmethod def _create_task( - remaining: List[int], + remaining: list[int], aws_session: AwsSession, device_arn: str, task_specification: Union[ @@ -247,7 +247,7 @@ def _create_task( s3_destination_folder: AwsSession.S3DestinationFolder, shots: int, poll_interval_seconds: float = AwsQuantumTask.DEFAULT_RESULTS_POLL_INTERVAL, - inputs: Dict[str, float] = None, + inputs: dict[str, float] = None, *args, **kwargs, ) -> AwsQuantumTask: @@ -278,7 +278,7 @@ def results( fail_unsuccessful: bool = False, max_retries: int = MAX_RETRIES, use_cached_value: bool = True, - ) -> List[AwsQuantumTask]: + ) -> list[AwsQuantumTask]: """Retrieves the result of every quantum task in the batch. Polling for results happens in parallel; this method returns when all quantum tasks @@ -295,7 +295,7 @@ def results( even when results have already been cached. Default: `True`. Returns: - List[AwsQuantumTask]: The results of all of the quantum tasks in the batch. + list[AwsQuantumTask]: The results of all of the quantum tasks in the batch. `FAILED`, `CANCELLED`, or timed out quantum tasks will have a result of None """ if not self._results or not use_cached_value: @@ -316,7 +316,7 @@ def results( return self._results @staticmethod - def _retrieve_results(tasks: List[AwsQuantumTask], max_workers: int) -> List[AwsQuantumTask]: + def _retrieve_results(tasks: list[AwsQuantumTask], max_workers: int) -> list[AwsQuantumTask]: with ThreadPoolExecutor(max_workers=max_workers) as executor: result_futures = [executor.submit(task.result) for task in tasks] return [future.result() for future in result_futures] @@ -362,8 +362,8 @@ def retry_unsuccessful_tasks(self) -> bool: return not self._unsuccessful @property - def tasks(self) -> List[AwsQuantumTask]: - """List[AwsQuantumTask]: The quantum tasks in this batch, as a list of AwsQuantumTask + def tasks(self) -> list[AwsQuantumTask]: + """list[AwsQuantumTask]: The quantum tasks in this batch, as a list of AwsQuantumTask objects""" return list(self._tasks) @@ -373,10 +373,10 @@ def size(self) -> int: return len(self._tasks) @property - def unfinished(self) -> Set[str]: + def unfinished(self) -> set[str]: """Gets all the IDs of all the quantum tasks in teh batch that have yet to complete. Returns: - Set[str]: The IDs of all the quantum tasks in the batch that have yet to complete. + set[str]: The IDs of all the quantum tasks in the batch that have yet to complete. """ with ThreadPoolExecutor(max_workers=self._max_workers) as executor: status_futures = {task.id: executor.submit(task.state) for task in self._tasks} @@ -390,7 +390,7 @@ def unfinished(self) -> Set[str]: return unfinished @property - def unsuccessful(self) -> Set[str]: - """Set[str]: The IDs of all the FAILED, CANCELLED, or timed out quantum tasks in the + def unsuccessful(self) -> set[str]: + """set[str]: The IDs of all the FAILED, CANCELLED, or timed out quantum tasks in the batch.""" return set(self._unsuccessful) diff --git a/src/braket/aws/aws_session.py b/src/braket/aws/aws_session.py index 5ce04a826..225140d8f 100644 --- a/src/braket/aws/aws_session.py +++ b/src/braket/aws/aws_session.py @@ -18,7 +18,7 @@ import os.path import re from pathlib import Path -from typing import Any, Dict, List, NamedTuple, Optional, Tuple +from typing import Any, NamedTuple, Optional import backoff import boto3 @@ -269,7 +269,7 @@ def _should_giveup(err: Exception) -> bool: jitter=backoff.full_jitter, giveup=_should_giveup.__func__, ) - def get_quantum_task(self, arn: str) -> Dict[str, Any]: + def get_quantum_task(self, arn: str) -> dict[str, Any]: """ Gets the quantum task. @@ -277,7 +277,7 @@ def get_quantum_task(self, arn: str) -> Dict[str, Any]: arn (str): The ARN of the quantum task to get. Returns: - Dict[str, Any]: The response from the Amazon Braket `GetQuantumTask` operation. + dict[str, Any]: The response from the Amazon Braket `GetQuantumTask` operation. """ response = self.braket_client.get_quantum_task( quantumTaskArn=arn, additionalAttributeNames=["QueueInfo"] @@ -316,7 +316,7 @@ def get_default_jobs_role(self) -> str: jitter=backoff.full_jitter, giveup=_should_giveup.__func__, ) - def get_job(self, arn: str) -> Dict[str, Any]: + def get_job(self, arn: str) -> dict[str, Any]: """ Gets the hybrid job. @@ -324,11 +324,11 @@ def get_job(self, arn: str) -> Dict[str, Any]: arn (str): The ARN of the hybrid job to get. Returns: - Dict[str, Any]: The response from the Amazon Braket `GetQuantumJob` operation. + dict[str, Any]: The response from the Amazon Braket `GetQuantumJob` operation. """ return self.braket_client.get_job(jobArn=arn, additionalAttributeNames=["QueueInfo"]) - def cancel_job(self, arn: str) -> Dict[str, Any]: + def cancel_job(self, arn: str) -> dict[str, Any]: """ Cancel the hybrid job. @@ -336,7 +336,7 @@ def cancel_job(self, arn: str) -> Dict[str, Any]: arn (str): The ARN of the hybrid job to cancel. Returns: - Dict[str, Any]: The response from the Amazon Braket `CancelJob` operation. + dict[str, Any]: The response from the Amazon Braket `CancelJob` operation. """ return self.braket_client.cancel_job(jobArn=arn) @@ -473,7 +473,7 @@ def copy_s3_directory(self, source_s3_path: str, destination_s3_path: str) -> No key.replace(source_prefix, destination_prefix, 1), ) - def list_keys(self, bucket: str, prefix: str) -> List[str]: + def list_keys(self, bucket: str, prefix: str) -> list[str]: """ Lists keys matching prefix in bucket. @@ -482,7 +482,7 @@ def list_keys(self, bucket: str, prefix: str) -> List[str]: prefix (str): The S3 path prefix to be matched Returns: - List[str]: A list of all keys matching the prefix in + list[str]: A list of all keys matching the prefix in the bucket. """ list_objects = self.s3_client.list_objects_v2( @@ -596,7 +596,7 @@ def _create_s3_bucket_if_it_does_not_exist(self, bucket_name: str, region: str) else: raise - def get_device(self, arn: str) -> Dict[str, Any]: + def get_device(self, arn: str) -> dict[str, Any]: """ Calls the Amazon Braket `get_device` API to retrieve device metadata. @@ -604,31 +604,31 @@ def get_device(self, arn: str) -> Dict[str, Any]: arn (str): The ARN of the device. Returns: - Dict[str, Any]: The response from the Amazon Braket `GetDevice` operation. + dict[str, Any]: The response from the Amazon Braket `GetDevice` operation. """ return self.braket_client.get_device(deviceArn=arn) def search_devices( self, - arns: Optional[List[str]] = None, - names: Optional[List[str]] = None, - types: Optional[List[str]] = None, - statuses: Optional[List[str]] = None, - provider_names: Optional[List[str]] = None, - ) -> List[Dict[str, Any]]: + arns: Optional[list[str]] = None, + names: Optional[list[str]] = None, + types: Optional[list[str]] = None, + statuses: Optional[list[str]] = None, + provider_names: Optional[list[str]] = None, + ) -> list[dict[str, Any]]: """ Get devices based on filters. The result is the AND of all the filters `arns`, `names`, `types`, `statuses`, `provider_names`. Args: - arns (Optional[List[str]]): device ARN list, default is `None`. - names (Optional[List[str]]): device name list, default is `None`. - types (Optional[List[str]]): device type list, default is `None`. - statuses (Optional[List[str]]): device status list, default is `None`. - provider_names (Optional[List[str]]): provider name list, default is `None`. + arns (Optional[list[str]]): device ARN list, default is `None`. + names (Optional[list[str]]): device name list, default is `None`. + types (Optional[list[str]]): device type list, default is `None`. + statuses (Optional[list[str]]): device status list, default is `None`. + provider_names (Optional[list[str]]): provider name list, default is `None`. Returns: - List[Dict[str, Any]]: The response from the Amazon Braket `SearchDevices` operation. + list[dict[str, Any]]: The response from the Amazon Braket `SearchDevices` operation. """ filters = [] if arns: @@ -665,7 +665,7 @@ def is_s3_uri(string: str) -> bool: return True @staticmethod - def parse_s3_uri(s3_uri: str) -> Tuple[str, str]: + def parse_s3_uri(s3_uri: str) -> tuple[str, str]: """ Parse S3 URI to get bucket and key @@ -673,7 +673,7 @@ def parse_s3_uri(s3_uri: str) -> Tuple[str, str]: s3_uri (str): S3 URI. Returns: - Tuple[str, str]: Bucket and Key tuple. + tuple[str, str]: Bucket and Key tuple. Raises: ValueError: Raises a ValueError if the provided string is not @@ -717,7 +717,7 @@ def describe_log_streams( log_stream_prefix: str, limit: int = None, next_token: Optional[str] = None, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Describes CloudWatch log streams in a log group with a given prefix. @@ -730,7 +730,7 @@ def describe_log_streams( Would have been received in a previous call. Returns: - Dict[str, Any]: Dicionary containing logStreams and nextToken + dict[str, Any]: Dicionary containing logStreams and nextToken """ log_stream_args = { "logGroupName": log_group, @@ -753,7 +753,7 @@ def get_log_events( start_time: int, start_from_head: bool = True, next_token: Optional[str] = None, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Gets CloudWatch log events from a given log stream. @@ -767,7 +767,7 @@ def get_log_events( Would have been received in a previous call. Returns: - Dict[str, Any]: Dicionary containing events, nextForwardToken, and nextBackwardToken + dict[str, Any]: Dicionary containing events, nextForwardToken, and nextBackwardToken """ log_events_args = { "logGroupName": log_group, diff --git a/src/braket/aws/queue_information.py b/src/braket/aws/queue_information.py index d45ed8761..109632751 100644 --- a/src/braket/aws/queue_information.py +++ b/src/braket/aws/queue_information.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Dict, Optional +from typing import Optional class QueueType(str, Enum): @@ -35,7 +35,7 @@ class QueueDepthInfo: Represents quantum tasks and hybrid jobs queue depth information. Attributes: - quantum_tasks (Dict[QueueType, str]): number of quantum tasks waiting + quantum_tasks (dict[QueueType, str]): number of quantum tasks waiting to run on a device. This includes both 'Normal' and 'Priority' tasks. For Example, {'quantum_tasks': {QueueType.NORMAL: '7', QueueType.PRIORITY: '3'}} jobs (str): number of hybrid jobs waiting to run on a device. Additionally, for QPUs if @@ -43,7 +43,7 @@ class QueueDepthInfo: running hybrid jobs. Example, 'jobs': '0 (1 prioritized job(s) running)' """ - quantum_tasks: Dict[QueueType, str] + quantum_tasks: dict[QueueType, str] jobs: str diff --git a/src/braket/circuits/angled_gate.py b/src/braket/circuits/angled_gate.py index f45bf6165..e453177a7 100644 --- a/src/braket/circuits/angled_gate.py +++ b/src/braket/circuits/angled_gate.py @@ -15,8 +15,9 @@ import copy import math +from collections.abc import Sequence from functools import singledispatch -from typing import List, Optional, Sequence, Union +from typing import Optional, Union from sympy import Float @@ -61,13 +62,13 @@ def __init__( self._parameters = [float(angle)] # explicit casting in case angle is e.g. np.float32 @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameters or bound values. Returns: - List[Union[FreeParameterExpression, float]]: The free parameters or fixed value + list[Union[FreeParameterExpression, float]]: The free parameters or fixed value associated with the object. """ return self._parameters @@ -93,11 +94,11 @@ def bind_values(self, **kwargs) -> AngledGate: """ raise NotImplementedError - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: """Returns the adjoint of this gate as a singleton list. Returns: - List[Gate]: A list containing the gate with negated angle. + list[Gate]: A list containing the gate with negated angle. """ gate_ascii_name_index = self.ascii_symbols[0].find("(") gate_ascii_name = self.ascii_symbols[0][:gate_ascii_name_index] @@ -166,13 +167,13 @@ def __init__( ] @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameters or bound values. Returns: - List[Union[FreeParameterExpression, float]]: The free parameters or fixed value + list[Union[FreeParameterExpression, float]]: The free parameters or fixed value associated with the object. """ return self._parameters @@ -212,11 +213,11 @@ def bind_values(self, **kwargs) -> AngledGate: """ raise NotImplementedError - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: """Returns the adjoint of this gate as a singleton list. Returns: - List[Gate]: A list containing the gate with negated angle. + list[Gate]: A list containing the gate with negated angle. """ raise NotImplementedError @@ -285,13 +286,13 @@ def __init__( ] @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameters or bound values. Returns: - List[Union[FreeParameterExpression, float]]: The free parameters or fixed value + list[Union[FreeParameterExpression, float]]: The free parameters or fixed value associated with the object. """ return self._parameters @@ -341,11 +342,11 @@ def bind_values(self, **kwargs) -> AngledGate: """ raise NotImplementedError - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: """Returns the adjoint of this gate as a singleton list. Returns: - List[Gate]: A list containing the gate with negated angle. + list[Gate]: A list containing the gate with negated angle. """ raise NotImplementedError diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 85de40a11..daef01793 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -14,7 +14,7 @@ from __future__ import annotations from functools import reduce -from typing import List, Tuple, Union +from typing import Union import braket.circuits.circuit as cir from braket.circuits.circuit_diagram import CircuitDiagram @@ -100,17 +100,17 @@ def build_diagram(circuit: cir.Circuit) -> str: @staticmethod def _ascii_group_items( circuit_qubits: QubitSet, - items: List[Union[Instruction, ResultType]], - ) -> List[Tuple[QubitSet, List[Instruction]]]: + items: list[Union[Instruction, ResultType]], + ) -> list[tuple[QubitSet, list[Instruction]]]: """ Group instructions in a moment for ASCII diagram Args: circuit_qubits (QubitSet): set of qubits in circuit - items (List[Union[Instruction, ResultType]]): list of instructions or result types + items (list[Union[Instruction, ResultType]]): list of instructions or result types Returns: - List[Tuple[QubitSet, List[Instruction]]]: list of grouped instructions or result types. + list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. """ groupings = [] for item in items: @@ -151,16 +151,16 @@ def _ascii_group_items( @staticmethod def _categorize_result_types( - result_types: List[ResultType], - ) -> Tuple[List[str], List[ResultType]]: + result_types: list[ResultType], + ) -> tuple[list[str], list[ResultType]]: """ Categorize result types into result types with target and those without. Args: - result_types (List[ResultType]): list of result types + result_types (list[ResultType]): list of result types Returns: - Tuple[List[str], List[ResultType]]: first element is a list of result types + tuple[list[str], list[ResultType]]: first element is a list of result types without `target` attribute; second element is a list of result types with `target` attribute """ @@ -175,7 +175,7 @@ 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]] ) -> str: """ Return a set of columns in the ASCII string diagram of the circuit for a list of items. @@ -183,7 +183,7 @@ def _ascii_diagram_column_set( Args: col_title (str): title of column set circuit_qubits (QubitSet): qubits in circuit - items (List[Union[Instruction, ResultType]]): list of instructions or result types + items (list[Union[Instruction, ResultType]]): list of instructions or result types Returns: str: An ASCII string diagram for the column set. @@ -220,14 +220,14 @@ 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]] ) -> str: """ Return a column in the ASCII string diagram of the circuit for a given list of items. Args: circuit_qubits (QubitSet): qubits in circuit - items (List[Union[Instruction, ResultType]]): list of instructions or result types + items (list[Union[Instruction, ResultType]]): list of instructions or result types Returns: str: An ASCII string diagram for the specified moment in time for a column. diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 66e406aa2..814444e75 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -1,5 +1,5 @@ from functools import singledispatch -from typing import List, Optional, Union +from typing import Optional, Union import numpy as np @@ -34,7 +34,7 @@ def __eq__(self, other): return self.state == other.state -BasisStateInput = Union[int, List[int], str, BasisState] +BasisStateInput = Union[int, list[int], str, BasisState] @singledispatch diff --git a/src/braket/circuits/braket_program_context.py b/src/braket/circuits/braket_program_context.py index 6d939882c..9d8c69afe 100644 --- a/src/braket/circuits/braket_program_context.py +++ b/src/braket/circuits/braket_program_context.py @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import List, Optional, Tuple, Union +from typing import Optional, Union import numpy as np from sympy import Expr @@ -56,18 +56,18 @@ 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: + def add_phase_instruction(self, target: tuple[int], phase_value: int) -> None: raise NotImplementedError def add_gate_instruction( - self, gate_name: str, target: Tuple[int], *params, ctrl_modifiers: List[int], power: float + self, gate_name: str, target: tuple[int], *params, ctrl_modifiers: list[int], power: float ) -> None: """Add Braket gate to the circuit. Args: gate_name (str): name of the built-in Braket gate. - target (Tuple[int]): control_qubits + target_qubits. - ctrl_modifiers (List[int]): Quantum state on which to control the + target (tuple[int]): control_qubits + target_qubits. + ctrl_modifiers (list[int]): Quantum state on which to control the operation. Must be a binary sequence of same length as number of qubits in `control-qubits` in target. 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 @@ -88,26 +88,26 @@ def add_gate_instruction( def add_custom_unitary( self, unitary: np.ndarray, - target: Tuple[int], + target: tuple[int], ) -> None: """Add a custom Unitary instruction to the circuit Args: unitary (np.ndarray): unitary matrix - target (Tuple[int]): control_qubits + target_qubits + target (tuple[int]): control_qubits + target_qubits """ instruction = Instruction(Unitary(unitary), target) self._circuit.add_instruction(instruction) def add_noise_instruction( - self, noise_instruction: str, target: List[int], probabilities: List[float] + self, noise_instruction: str, target: list[int], probabilities: list[float] ) -> None: """Method to add a noise instruction to the circuit Args: noise_instruction (str): The name of the noise operation - target (List[int]): The target qubit or qubits to which the noise operation is applied. - probabilities (List[float]): The probabilities associated with each possible outcome + target (list[int]): The target qubit or qubits to which the noise operation is applied. + probabilities (list[float]): The probabilities associated with each possible outcome of the noise operation. """ instruction = Instruction( @@ -115,12 +115,12 @@ def add_noise_instruction( ) self._circuit.add_instruction(instruction) - def add_kraus_instruction(self, matrices: List[np.ndarray], target: List[int]) -> None: + def add_kraus_instruction(self, matrices: list[np.ndarray], target: list[int]) -> None: """Method to add a Kraus instruction to the circuit Args: - matrices (List[ndarray]): The matrices defining the Kraus operation - target (List[int]): The target qubit or qubits to which the Kraus operation is applied. + matrices (list[ndarray]): The matrices defining the Kraus operation + target (list[int]): The target qubit or qubits to which the Kraus operation is applied. """ instruction = Instruction(Kraus(matrices), target) self._circuit.add_instruction(instruction) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 34bde9b98..5d191c69b 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -14,8 +14,9 @@ from __future__ import annotations import warnings +from collections.abc import Callable, Iterable from numbers import Number -from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union +from typing import Any, Optional, TypeVar, Union import numpy as np import oqpy @@ -140,9 +141,9 @@ def __init__(self, addable: AddableTypes = None, *args, **kwargs): """ self._moments: Moments = Moments() - self._result_types: Dict[ResultType] = {} - self._qubit_observable_mapping: Dict[Union[int, Circuit._ALL_QUBITS], Observable] = {} - self._qubit_observable_target_mapping: Dict[int, Tuple[int]] = {} + self._result_types: dict[ResultType] = {} + self._qubit_observable_mapping: dict[Union[int, Circuit._ALL_QUBITS], Observable] = {} + self._qubit_observable_target_mapping: dict[int, tuple[int]] = {} self._qubit_observable_set = set() self._parameters = set() self._observables_simultaneously_measurable = True @@ -157,21 +158,21 @@ def depth(self) -> int: return self._moments.depth @property - def instructions(self) -> List[Instruction]: + def instructions(self) -> list[Instruction]: """Iterable[Instruction]: Get an `iterable` of instructions in the circuit.""" return list(self._moments.values()) @property - def result_types(self) -> List[ResultType]: - """List[ResultType]: Get a list of requested result types in the circuit.""" + def result_types(self) -> list[ResultType]: + """list[ResultType]: Get a list of requested result types in the circuit.""" return list(self._result_types.keys()) @property - def basis_rotation_instructions(self) -> List[Instruction]: + def basis_rotation_instructions(self) -> list[Instruction]: """Gets a list of basis rotation instructions. Returns: - List[Instruction]: Get a list of basis rotation instructions in the circuit. + list[Instruction]: Get a list of basis rotation instructions in the circuit. These basis rotation instructions are added if result types are requested for an observable other than Pauli-Z. @@ -199,8 +200,8 @@ def basis_rotation_instructions(self) -> List[Instruction]: @staticmethod def _observable_to_instruction( - observable: Observable, target_list: List[int] - ) -> List[Instruction]: + observable: Observable, target_list: list[int] + ) -> list[Instruction]: return [Instruction(gate, target_list) for gate in observable.basis_rotation_gates] @property @@ -223,12 +224,12 @@ def qubits(self) -> QubitSet: return QubitSet(self._moments.qubits.union(self._qubit_observable_set)) @property - def parameters(self) -> Set[FreeParameter]: + def parameters(self) -> set[FreeParameter]: """ Gets a set of the parameters in the Circuit. Returns: - Set[FreeParameter]: The `FreeParameters` in the Circuit. + set[FreeParameter]: The `FreeParameters` in the Circuit. """ return self._parameters @@ -236,7 +237,7 @@ def add_result_type( self, result_type: ResultType, target: QubitSetInput = None, - target_mapping: Dict[QubitInput, QubitInput] = None, + target_mapping: dict[QubitInput, QubitInput] = None, ) -> Circuit: """ Add a requested result type to `self`, returns `self` for chaining ability. @@ -246,7 +247,7 @@ def add_result_type( target (QubitSetInput): Target qubits for the `result_type`. Default = `None`. - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the `result_type.target`. Key is the qubit in `result_type.target` and the value is what the key will be changed to. Default = `None`. @@ -376,7 +377,7 @@ def _add_to_qubit_observable_mapping( @staticmethod def _tensor_product_index_dict( observable: TensorProduct, observable_target: QubitSet - ) -> Dict[int, Tuple[Observable, Tuple[int, ...]]]: + ) -> dict[int, tuple[Observable, tuple[int, ...]]]: obj_dict = {} i = 0 factors = list(observable.factors) @@ -400,7 +401,7 @@ def add_instruction( self, instruction: Instruction, target: QubitSetInput = None, - target_mapping: Dict[QubitInput, QubitInput] = None, + target_mapping: dict[QubitInput, QubitInput] = None, ) -> Circuit: """ Add an instruction to `self`, returns `self` for chaining ability. @@ -411,7 +412,7 @@ def add_instruction( `instruction`. If a single qubit gate, an instruction is created for every index in `target`. Default = `None`. - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the `instruction.target`. Key is the qubit in `instruction.target` and the value is what the key will be changed to. Default = `None`. @@ -492,7 +493,7 @@ def add_circuit( self, circuit: Circuit, target: QubitSetInput = None, - target_mapping: Dict[QubitInput, QubitInput] = None, + target_mapping: dict[QubitInput, QubitInput] = None, ) -> Circuit: """ Add a `circuit` to self, returns self for chaining ability. @@ -503,7 +504,7 @@ def add_circuit( supplied circuit. This is a macro over `target_mapping`; `target` is converted to a `target_mapping` by zipping together a sorted `circuit.qubits` and `target`. Default = `None`. - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the qubits of `circuit.instructions`. Key is the qubit to map, and the value is what to change it to. Default = `None`. @@ -568,7 +569,7 @@ def add_verbatim_box( self, verbatim_circuit: Circuit, target: QubitSetInput = None, - target_mapping: Dict[QubitInput, QubitInput] = None, + target_mapping: dict[QubitInput, QubitInput] = None, ) -> Circuit: """ Add a verbatim `circuit` to self, that is, ensures that `circuit` is not modified in any way @@ -580,7 +581,7 @@ def add_verbatim_box( supplied circuit. This is a macro over `target_mapping`; `target` is converted to a `target_mapping` by zipping together a sorted `circuit.qubits` and `target`. Default = `None`. - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the qubits of `circuit.instructions`. Key is the qubit to map, and the value is what to change it to. Default = `None`. @@ -636,8 +637,8 @@ def add_verbatim_box( def apply_gate_noise( self, - noise: Union[Type[Noise], Iterable[Type[Noise]]], - target_gates: Optional[Union[Type[Gate], Iterable[Type[Gate]]]] = None, + noise: Union[type[Noise], Iterable[type[Noise]]], + target_gates: Optional[Union[type[Gate], Iterable[type[Gate]]]] = None, target_unitary: np.ndarray = None, target_qubits: Optional[QubitSetInput] = None, ) -> Circuit: @@ -663,9 +664,9 @@ def apply_gate_noise( only applied to gates with the same qubit_count in target_qubits. Args: - noise (Union[Type[Noise], Iterable[Type[Noise]]]): Noise channel(s) to be applied + noise (Union[type[Noise], Iterable[type[Noise]]]): Noise channel(s) to be applied to the circuit. - target_gates (Optional[Union[Type[Gate], Iterable[Type[Gate]]]]): Gate class or + target_gates (Optional[Union[type[Gate], Iterable[type[Gate]]]]): Gate class or List of Gate classes which `noise` is applied to. Default=None. target_unitary (ndarray): matrix of the target unitary gates. Default=None. target_qubits (Optional[QubitSetInput]): Index or indices of qubit(s). @@ -780,7 +781,7 @@ def apply_gate_noise( def apply_initialization_noise( self, - noise: Union[Type[Noise], Iterable[Type[Noise]]], + noise: Union[type[Noise], Iterable[type[Noise]]], target_qubits: Optional[QubitSetInput] = None, ) -> Circuit: """Apply `noise` at the beginning of the circuit for every qubit (default) or @@ -792,7 +793,7 @@ def apply_initialization_noise( to `noise.qubit_count`. Args: - noise (Union[Type[Noise], Iterable[Type[Noise]]]): Noise channel(s) to be applied + noise (Union[type[Noise], Iterable[type[Noise]]]): Noise channel(s) to be applied to the circuit. target_qubits (Optional[QubitSetInput]): Index or indices of qubit(s). Default=None. @@ -847,13 +848,13 @@ def apply_initialization_noise( return apply_noise_to_moments(self, noise, target_qubits, "initialization") - def make_bound_circuit(self, param_values: Dict[str, Number], strict: bool = False) -> Circuit: + def make_bound_circuit(self, param_values: dict[str, Number], strict: bool = False) -> Circuit: """ Binds FreeParameters based upon their name and values passed in. If parameters share the same name, all the parameters of that name will be set to the mapped value. Args: - param_values (Dict[str, Number]): A mapping of FreeParameter names + param_values (dict[str, Number]): A mapping of FreeParameter names to a value to assign to them. strict (bool): If True, raises a ValueError if none of the FreeParameters in param_values appear in the circuit. False by default." @@ -866,12 +867,12 @@ def make_bound_circuit(self, param_values: Dict[str, Number], strict: bool = Fal self._validate_parameters(param_values) return self._use_parameter_value(param_values) - def _validate_parameters(self, parameter_values: Dict[str, Number]) -> None: + def _validate_parameters(self, parameter_values: dict[str, Number]) -> None: """ This runs a check to see that the parameters are in the Circuit. Args: - parameter_values (Dict[str, Number]): A mapping of FreeParameter names + parameter_values (dict[str, Number]): A mapping of FreeParameter names to a value to assign to them. Raises: @@ -885,12 +886,12 @@ def _validate_parameters(self, parameter_values: Dict[str, Number]) -> None: if param not in parameter_strings: raise ValueError(f"No parameter in the circuit named: {param}") - def _use_parameter_value(self, param_values: Dict[str, Number]) -> Circuit: + def _use_parameter_value(self, param_values: dict[str, Number]) -> Circuit: """ Creates a Circuit that uses the parameter values passed in. Args: - param_values (Dict[str, Number]): A mapping of FreeParameter names + param_values (dict[str, Number]): A mapping of FreeParameter names to a value to assign to them. Returns: @@ -931,7 +932,7 @@ def _validate_parameter_value(val: Any) -> None: def apply_readout_noise( self, - noise: Union[Type[Noise], Iterable[Type[Noise]]], + noise: Union[type[Noise], Iterable[type[Noise]]], target_qubits: Optional[QubitSetInput] = None, ) -> Circuit: """Apply `noise` right before measurement in every qubit (default) or target_qubits`. @@ -942,7 +943,7 @@ def apply_readout_noise( to `noise.qubit_count`. Args: - noise (Union[Type[Noise], Iterable[Type[Noise]]]): Noise channel(s) to be applied + noise (Union[type[Noise], Iterable[type[Noise]]]): Noise channel(s) to be applied to the circuit. target_qubits (Optional[QubitSetInput]): Index or indices of qubit(s). Default=None. @@ -1081,12 +1082,12 @@ def adjoint(self) -> Circuit: circ.add_result_type(result_type) return circ - def diagram(self, circuit_diagram_class: Type = AsciiCircuitDiagram) -> str: + def diagram(self, circuit_diagram_class: type = AsciiCircuitDiagram) -> str: """ Get a diagram for the current circuit. Args: - circuit_diagram_class (Type): A `CircuitDiagram` class that builds the + circuit_diagram_class (type): A `CircuitDiagram` class that builds the diagram for this circuit. Default = `AsciiCircuitDiagram`. Returns: @@ -1098,7 +1099,7 @@ def to_ir( self, ir_type: IRType = IRType.JAQCD, serialization_properties: SerializationProperties = None, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] = None, + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] = None, ) -> Union[OpenQasmProgram, JaqcdProgram]: """ Converts the circuit into the canonical intermediate representation. @@ -1110,7 +1111,7 @@ def to_ir( serialization_properties (SerializationProperties): The serialization properties to use while serializing the object to the IR representation. The serialization properties supplied must correspond to the supplied `ir_type`. Defaults to None. - gate_definitions (Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]]): The + gate_definitions (Optional[dict[tuple[Gate, QubitSet], PulseSequence]]): The calibration data for the device. default: None. Returns: @@ -1140,14 +1141,14 @@ def to_ir( @staticmethod def from_ir( - source: Union[str, OpenQasmProgram], inputs: Optional[Dict[str, io_type]] = None + source: Union[str, OpenQasmProgram], inputs: Optional[dict[str, io_type]] = None ) -> Circuit: """ Converts an OpenQASM program to a Braket Circuit object. Args: source (Union[str, OpenQasmProgram]): OpenQASM string. - inputs (Optional[Dict[str, io_type]]): Inputs to the circuit. + inputs (Optional[dict[str, io_type]]): Inputs to the circuit. Returns: Circuit: Braket Circuit implementing the OpenQASM program. @@ -1182,7 +1183,7 @@ def _to_jaqcd(self) -> JaqcdProgram: def _to_openqasm( self, serialization_properties: OpenQASMSerializationProperties, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], ) -> OpenQasmProgram: ir_instructions = self._create_openqasm_header(serialization_properties, gate_definitions) openqasm_ir_type = IRType.OPENQASM @@ -1219,8 +1220,8 @@ def _to_openqasm( def _create_openqasm_header( self, serialization_properties: OpenQASMSerializationProperties, - gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]], - ) -> List[str]: + gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]], + ) -> list[str]: ir_instructions = ["OPENQASM 3.0;"] for parameter in self.parameters: ir_instructions.append(f"input float {parameter};") @@ -1243,9 +1244,9 @@ def _create_openqasm_header( def _validate_gate_calbrations_uniqueness( self, - gate_definitions: Dict[Tuple[Gate, QubitSet], PulseSequence], - frames: Dict[Frame], - waveforms: Dict[ArbitraryWaveform], + gate_definitions: dict[tuple[Gate, QubitSet], PulseSequence], + frames: dict[Frame], + waveforms: dict[ArbitraryWaveform], ) -> None: for key, calibration in gate_definitions.items(): for frame in calibration._frames.values(): @@ -1256,7 +1257,7 @@ def _validate_gate_calbrations_uniqueness( waveforms[waveform.id] = waveform def _generate_frame_wf_defcal_declarations( - self, gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] + self, gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] ) -> Optional[str]: program = oqpy.Program(None) @@ -1302,8 +1303,8 @@ def _generate_frame_wf_defcal_declarations( return None def _get_frames_waveforms_from_instrs( - self, gate_definitions: Optional[Dict[Tuple[Gate, QubitSet], PulseSequence]] - ) -> Tuple[Dict[Frame], Dict[ArbitraryWaveform]]: + self, gate_definitions: Optional[dict[tuple[Gate, QubitSet], PulseSequence]] + ) -> tuple[dict[Frame], dict[ArbitraryWaveform]]: from braket.circuits.gates import PulseGate frames = {} @@ -1326,9 +1327,9 @@ def _get_frames_waveforms_from_instrs( def _add_fixed_argument_calibrations( self, - gate_definitions: Dict[Tuple[Gate, QubitSet], PulseSequence], + gate_definitions: dict[tuple[Gate, QubitSet], PulseSequence], instruction: Instruction, - ) -> Dict[Tuple[Gate, QubitSet], PulseSequence]: + ) -> dict[tuple[Gate, QubitSet], PulseSequence]: """Adds calibrations with arguments set to the instruction parameter values Given the collection of parameters in instruction.operator, this function looks for matching @@ -1341,12 +1342,12 @@ def _add_fixed_argument_calibrations( If N=0, we ignore it as it will not be removed by _generate_frame_wf_defcal_declarations. Args: - gate_definitions (Dict[Tuple[Gate, QubitSet], PulseSequence]): a dictionary of + gate_definitions (dict[tuple[Gate, QubitSet], PulseSequence]): a dictionary of calibrations instruction (Instruction): a Circuit instruction Returns: - Dict[Tuple[Gate, QubitSet], PulseSequence]: additional calibrations + dict[tuple[Gate, QubitSet], PulseSequence]: additional calibrations Raises: NotImplementedError: in two cases: (i) if the instruction contains unbound parameters diff --git a/src/braket/circuits/circuit_diagram.py b/src/braket/circuits/circuit_diagram.py index ee09d73f7..5b156d290 100644 --- a/src/braket/circuits/circuit_diagram.py +++ b/src/braket/circuits/circuit_diagram.py @@ -10,6 +10,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. + from __future__ import annotations from abc import ABC, abstractmethod diff --git a/src/braket/circuits/compiler_directive.py b/src/braket/circuits/compiler_directive.py index 3ca677ff7..d5b5591fe 100644 --- a/src/braket/circuits/compiler_directive.py +++ b/src/braket/circuits/compiler_directive.py @@ -13,7 +13,8 @@ from __future__ import annotations -from typing import Any, Sequence, Tuple +from collections.abc import Sequence +from typing import Any from braket.circuits.operator import Operator from braket.circuits.serialization import IRType, SerializationProperties @@ -41,8 +42,8 @@ def name(self) -> str: return self.__class__.__name__ @property - def ascii_symbols(self) -> Tuple[str, ...]: - """Tuple[str, ...]: Returns the ascii symbols for the compiler directive.""" + def ascii_symbols(self) -> tuple[str, ...]: + """tuple[str, ...]: Returns the ascii symbols for the compiler directive.""" return self._ascii_symbols def to_ir( diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 135a892c3..718e3344d 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -13,8 +13,9 @@ from __future__ import annotations +from collections.abc import Sequence from itertools import groupby -from typing import Any, List, Optional, Sequence, Tuple, Type +from typing import Any, Optional from braket.circuits.basis_state import BasisState, BasisStateInput from braket.circuits.quantum_operator import QuantumOperator @@ -55,13 +56,13 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): def _qasm_name(self) -> NotImplementedError: raise NotImplementedError() - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: """Returns a list of gates that implement the adjoint of this gate. This is a list because some gates do not have an inverse defined by a single existing gate. Returns: - List[Gate]: The gates comprising the adjoint of this gate. + list[Gate]: The gates comprising the adjoint of this gate. """ raise NotImplementedError(f"Gate {self.name} does not have adjoint implemented") @@ -200,8 +201,8 @@ def _to_openqasm( ) @property - def ascii_symbols(self) -> Tuple[str, ...]: - """Tuple[str, ...]: Returns the ascii symbols for the quantum operator.""" + def ascii_symbols(self) -> tuple[str, ...]: + """tuple[str, ...]: Returns the ascii symbols for the quantum operator.""" return self._ascii_symbols def __eq__(self, other): @@ -214,10 +215,10 @@ def __hash__(self): return hash((self.name, self.qubit_count)) @classmethod - def register_gate(cls, gate: Type[Gate]) -> None: + def register_gate(cls, gate: type[Gate]) -> None: """Register a gate implementation by adding it into the Gate class. Args: - gate (Type[Gate]): Gate class to register. + gate (type[Gate]): Gate class to register. """ setattr(cls, gate.__name__, gate) diff --git a/src/braket/circuits/gate_calibrations.py b/src/braket/circuits/gate_calibrations.py index 3c5abefd7..6cbdd97d1 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, Dict, List, Optional, Tuple +from typing import Any, Optional from braket.circuits.gate import Gate from braket.circuits.serialization import ( @@ -35,23 +35,23 @@ class GateCalibrations: def __init__( self, - pulse_sequences: Dict[Tuple[Gate, QubitSet], PulseSequence], + pulse_sequences: dict[tuple[Gate, QubitSet], PulseSequence], ): """ Args: - pulse_sequences (Dict[Tuple[Gate, QubitSet], PulseSequence]): A mapping containing a key of + pulse_sequences (dict[tuple[Gate, QubitSet], PulseSequence]): A mapping containing a key of `(Gate, QubitSet)` mapped to the corresponding pulse sequence. """ # noqa: E501 - self.pulse_sequences: Dict[Tuple[Gate, QubitSet], PulseSequence] = pulse_sequences + self.pulse_sequences: dict[tuple[Gate, QubitSet], PulseSequence] = pulse_sequences @property - def pulse_sequences(self) -> Dict[Tuple[Gate, QubitSet], PulseSequence]: + def pulse_sequences(self) -> dict[tuple[Gate, QubitSet], PulseSequence]: """ Gets the mapping of (Gate, Qubit) to the corresponding `PulseSequence`. Returns: - Dict[Tuple[Gate, QubitSet], PulseSequence]: The calibration data Dictionary. + dict[tuple[Gate, QubitSet], PulseSequence]: The calibration data Dictionary. """ return self._pulse_sequences @@ -64,7 +64,7 @@ def pulse_sequences(self, value: Any) -> None: value(Any): The value for the pulse_sequences property to be set to. Raises: - TypeError: Raised if the type is not Dict[Tuple[Gate, QubitSet], PulseSequence] + TypeError: Raised if the type is not dict[tuple[Gate, QubitSet], PulseSequence] """ if isinstance(value, dict) and all( @@ -75,7 +75,7 @@ def pulse_sequences(self, value: Any) -> None: else: raise TypeError( "The value for pulse_sequence must be of type: " - "Dict[Tuple[Gate, QubitSet], PulseSequence]" + "dict[tuple[Gate, QubitSet], PulseSequence]" ) def copy(self) -> GateCalibrations: @@ -91,13 +91,13 @@ def __len__(self): return len(self._pulse_sequences) def filter( - self, gates: Optional[List[Gate]] = None, qubits: Optional[QubitSet] = None + self, gates: Optional[list[Gate]] = None, qubits: Optional[QubitSet] = None ) -> Optional[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. + gates (Optional[list[Gate]]): An optional list of gates to filter on. qubits (Optional[QubitSet]): An optional `QubitSet` to filter on. Returns: @@ -114,12 +114,12 @@ def filter( {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: Optional[tuple[Gate, QubitSet]] = 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 (Optional[tuple[Gate, QubitSet]]): An optional key to get a specific defcal. Default: None Returns: @@ -143,7 +143,7 @@ def to_ir(self, calibration_key: Optional[Tuple[Gate, QubitSet]] = None) -> str: ) return defcal - def _def_cal_gate(self, gate_key: Tuple[Gate, QubitSet]) -> str: + def _def_cal_gate(self, gate_key: tuple[Gate, QubitSet]) -> str: return " ".join( [ "defcal", diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index a68d9eade..dbc2e1a0b 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -13,8 +13,9 @@ from __future__ import annotations +from collections.abc import Iterable from copy import deepcopy -from typing import Any, Iterable, List, Optional, Union +from typing import Any, Optional, Union import numpy as np from oqpy import Program @@ -68,7 +69,7 @@ def __init__(self): def _qasm_name(self) -> str: return "h" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [H()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -133,7 +134,7 @@ def __init__(self): def _qasm_name(self) -> str: return "i" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [I()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -198,7 +199,7 @@ def __init__(self): def _qasm_name(self) -> str: return "x" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [X()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -263,7 +264,7 @@ def __init__(self): def _qasm_name(self) -> str: return "y" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Y()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -328,7 +329,7 @@ def __init__(self): def _qasm_name(self) -> str: return "z" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Z()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -393,7 +394,7 @@ def __init__(self): def _qasm_name(self) -> str: return "s" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Si()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -458,7 +459,7 @@ def __init__(self): def _qasm_name(self) -> str: return "si" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [S()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -523,7 +524,7 @@ def __init__(self): def _qasm_name(self) -> str: return "t" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Ti()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -588,7 +589,7 @@ def __init__(self): def _qasm_name(self) -> str: return "ti" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [T()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -653,7 +654,7 @@ def __init__(self): def _qasm_name(self) -> str: return "v" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Vi()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -718,7 +719,7 @@ def __init__(self): def _qasm_name(self) -> str: return "vi" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [V()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1103,7 +1104,7 @@ def __init__(self): def _qasm_name(self) -> str: return "cnot" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [CNot()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1163,7 +1164,7 @@ def __init__(self): def _qasm_name(self) -> str: return "swap" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Swap()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1238,7 +1239,7 @@ def __init__(self): def _qasm_name(self) -> str: return "iswap" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [self, self, self] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1773,7 +1774,7 @@ def __init__(self): def _qasm_name(self) -> str: return "cv" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [self, self, self] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1833,7 +1834,7 @@ def __init__(self): def _qasm_name(self) -> str: return "cy" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [CY()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1893,7 +1894,7 @@ def __init__(self): def _qasm_name(self) -> str: return "cz" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [CZ()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -1945,7 +1946,7 @@ def __init__(self): def _qasm_name(self) -> str: return "ecr" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [ECR()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -2304,7 +2305,7 @@ def __init__(self): def _qasm_name(self) -> str: return "ccnot" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [CCNot()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -2387,7 +2388,7 @@ def __init__(self): def _qasm_name(self) -> str: return "cswap" - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [CSwap()] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -2476,7 +2477,7 @@ def to_matrix(self) -> np.ndarray: ] ) - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [GPi(self.angle)] @staticmethod @@ -2555,7 +2556,7 @@ def to_matrix(self) -> np.ndarray: ] ) / np.sqrt(2) - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [GPi2(self.angle + np.pi)] @staticmethod @@ -2665,7 +2666,7 @@ def to_matrix(self) -> np.ndarray: ] ) - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [MS(self.angle_1 + np.pi, self.angle_2, self.angle_3)] @staticmethod @@ -2754,7 +2755,7 @@ def __init__(self, matrix: np.ndarray, display_name: str = "U"): def to_matrix(self) -> np.ndarray: return np.array(self._matrix) - def adjoint(self) -> List[Gate]: + def adjoint(self) -> list[Gate]: return [Unitary(self._matrix.conj().T, display_name=f"({self.ascii_symbols})^†")] def _to_jaqcd(self, target: QubitSet) -> Any: @@ -2785,7 +2786,7 @@ def __hash__(self): return hash((self.name, str(self._matrix), self.qubit_count)) @staticmethod - def _transform_matrix_to_ir(matrix: np.ndarray) -> List: + def _transform_matrix_to_ir(matrix: np.ndarray) -> list: return [[[element.real, element.imag] for element in row] for row in matrix.tolist()] @staticmethod @@ -2847,7 +2848,7 @@ def pulse_sequence(self) -> PulseSequence: return self._pulse_sequence @property - def parameters(self) -> List[FreeParameter]: + def parameters(self) -> list[FreeParameter]: """Returns the list of `FreeParameter` s associated with the gate.""" return list(self._pulse_sequence.parameters) diff --git a/src/braket/circuits/instruction.py b/src/braket/circuits/instruction.py index 6211e1221..18dc49e4a 100644 --- a/src/braket/circuits/instruction.py +++ b/src/braket/circuits/instruction.py @@ -13,7 +13,7 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional from braket.circuits.basis_state import BasisState, BasisStateInput from braket.circuits.compiler_directive import CompilerDirective @@ -135,13 +135,13 @@ def power(self) -> float: """ return self._power - def adjoint(self) -> List[Instruction]: + def adjoint(self) -> list[Instruction]: """Returns a list of Instructions implementing adjoint of this instruction's own operator This operation only works on Gate operators and compiler directives. Returns: - List[Instruction]: A list of new instructions that comprise the adjoint of this operator + list[Instruction]: A list of new instructions that comprise the adjoint of this operator Raises: NotImplementedError: If `operator` is not of type `Gate` or `CompilerDirective` @@ -195,15 +195,15 @@ def to_ir( ) @property - def ascii_symbols(self) -> Tuple[str, ...]: - """Tuple[str, ...]: Returns the ascii symbols for the instruction's operator.""" + def ascii_symbols(self) -> tuple[str, ...]: + """tuple[str, ...]: Returns the ascii symbols for the instruction's operator.""" return self._operator.ascii_symbols def copy( self, - target_mapping: Dict[QubitInput, QubitInput] = None, + target_mapping: dict[QubitInput, QubitInput] = None, target: QubitSetInput = None, - control_mapping: Dict[QubitInput, QubitInput] = None, + control_mapping: dict[QubitInput, QubitInput] = None, control: QubitSetInput = None, control_state: Optional[BasisStateInput] = None, power: float = 1, @@ -217,11 +217,11 @@ def copy( Same relationship holds for `control_mapping`. Args: - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the target. Key is the qubit in this `target` and the value is what the key is changed to. Default = `None`. target (QubitSetInput): Target qubits for the new instruction. Default is None. - control_mapping (Dict[QubitInput, QubitInput]): A dictionary of + control_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the control. Key is the qubit in this `control` and the value is what the key is changed to. Default = `None`. control (QubitSetInput): Control qubits for the new instruction. Default is None. diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index 70cc81bcd..a4c31339b 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -13,20 +13,10 @@ from __future__ import annotations +from collections import OrderedDict +from collections.abc import ItemsView, Iterable, KeysView, Mapping, ValuesView from enum import Enum -from typing import ( - Any, - Dict, - ItemsView, - Iterable, - KeysView, - List, - Mapping, - NamedTuple, - OrderedDict, - Union, - ValuesView, -) +from typing import Any, NamedTuple, Union from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.instruction import Instruction @@ -112,7 +102,7 @@ class Moments(Mapping[MomentsKey, Instruction]): def __init__(self, instructions: Iterable[Instruction] = None): self._moments: OrderedDict[MomentsKey, Instruction] = OrderedDict() - self._max_times: Dict[Qubit, int] = {} + self._max_times: dict[Qubit, int] = {} self._qubits = QubitSet() self._depth = 0 self._time_all_qubits = -1 @@ -141,12 +131,12 @@ def qubits(self) -> QubitSet: """ return self._qubits - def time_slices(self) -> Dict[int, List[Instruction]]: + def time_slices(self) -> dict[int, list[Instruction]]: """ Get instructions keyed by time. Returns: - Dict[int, List[Instruction]]: Key is the time and value is a list of instructions that + dict[int, list[Instruction]]: Key is the time and value is a list of instructions that occur at that moment in time. The order of instructions is in no particular order. Note: diff --git a/src/braket/circuits/noise.py b/src/braket/circuits/noise.py index 498f70fdc..8e5b9cc73 100644 --- a/src/braket/circuits/noise.py +++ b/src/braket/circuits/noise.py @@ -13,7 +13,8 @@ from __future__ import annotations -from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Type, Union +from collections.abc import Iterable, Sequence +from typing import Any, Optional, Union import numpy as np @@ -163,10 +164,10 @@ def from_dict(cls, noise: dict) -> Noise: raise NotImplementedError @classmethod - def register_noise(cls, noise: Type[Noise]) -> None: + def register_noise(cls, noise: type[Noise]) -> None: """Register a noise implementation by adding it into the Noise class. Args: - noise (Type[Noise]): Noise class to register. + noise (type[Noise]): Noise class to register. """ setattr(cls, noise.__name__, noise) @@ -220,13 +221,13 @@ def __str__(self): return f"{self.name}({self.probability})" @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameter expressions or bound values. Returns: - List[Union[FreeParameterExpression, float]]: The free parameter expressions + list[Union[FreeParameterExpression, float]]: The free parameter expressions or fixed values associated with the object. """ return [self._probability] @@ -343,14 +344,14 @@ class MultiQubitPauliNoise(Noise, Parameterizable): def __init__( self, - probabilities: Dict[str, Union[FreeParameterExpression, float]], + probabilities: dict[str, Union[FreeParameterExpression, float]], qubit_count: Optional[int], ascii_symbols: Sequence[str], ): """[summary] Args: - probabilities (Dict[str, Union[FreeParameterExpression, float]]): A dictionary with + probabilities (dict[str, Union[FreeParameterExpression, float]]): A dictionary with Pauli strings as keys and the probabilities as values, i.e. {"XX": 0.1. "IZ": 0.2}. qubit_count (Optional[int]): The number of qubits the Pauli noise acts on. ascii_symbols (Sequence[str]): ASCII string symbols for the noise. These are used when @@ -402,7 +403,7 @@ def __init__( @classmethod def _validate_pauli_string( - cls, pauli_str: str, qubit_count: int, allowed_substrings: Set[str] + cls, pauli_str: str, qubit_count: int, allowed_substrings: set[str] ) -> None: if not isinstance(pauli_str, str): raise TypeError(f"Type of {pauli_str} was not a string.") @@ -436,15 +437,15 @@ def __eq__(self, other): return False @property - def probabilities(self) -> Dict[str, float]: + def probabilities(self) -> dict[str, float]: """A map of a Pauli string to its corresponding probability. Returns: - Dict[str, float]: A map of a Pauli string to its corresponding probability. + dict[str, float]: A map of a Pauli string to its corresponding probability. """ return self._probabilities @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameter expressions or bound values. @@ -452,7 +453,7 @@ def parameters(self) -> List[Union[FreeParameterExpression, float]]: Parameters are in alphabetical order of the Pauli strings in `probabilities`. Returns: - List[Union[FreeParameterExpression, float]]: The free parameter expressions + list[Union[FreeParameterExpression, float]]: The free parameter expressions or fixed values associated with the object. """ return [ @@ -596,7 +597,7 @@ def __eq__(self, other): return False @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameter expressions or bound values. @@ -604,7 +605,7 @@ def parameters(self) -> List[Union[FreeParameterExpression, float]]: Parameters are in the order [probX, probY, probZ] Returns: - List[Union[FreeParameterExpression, float]]: The free parameter expressions + list[Union[FreeParameterExpression, float]]: The free parameter expressions or fixed values associated with the object. """ return self._parameters @@ -689,13 +690,13 @@ def __str__(self): return f"{self.name}({self.gamma})" @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameter expressions or bound values. Returns: - List[Union[FreeParameterExpression, float]]: The free parameter expressions + list[Union[FreeParameterExpression, float]]: The free parameter expressions or fixed values associated with the object. """ return [self._gamma] @@ -791,7 +792,7 @@ def __str__(self): return f"{self.name}({self.gamma}, {self.probability})" @property - def parameters(self) -> List[Union[FreeParameterExpression, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, float]]: """ Returns the parameters associated with the object, either unbound free parameter expressions or bound values. @@ -799,7 +800,7 @@ def parameters(self) -> List[Union[FreeParameterExpression, float]]: Parameters are in the order [gamma, probability] Returns: - List[Union[FreeParameterExpression, float]]: The free parameter expressions + list[Union[FreeParameterExpression, float]]: The free parameter expressions or fixed values associated with the object. """ return [self.gamma, self.probability] diff --git a/src/braket/circuits/noise_helpers.py b/src/braket/circuits/noise_helpers.py index 2238a2d61..06c0b3620 100644 --- a/src/braket/circuits/noise_helpers.py +++ b/src/braket/circuits/noise_helpers.py @@ -14,7 +14,8 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Iterable, List, Optional, Tuple, Type, Union +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any, Optional, Union import numpy as np @@ -42,27 +43,27 @@ def no_noise_applied_warning(noise_applied: bool) -> None: ) -def wrap_with_list(an_item: Any) -> List[Any]: +def wrap_with_list(an_item: Any) -> list[Any]: """Helper function to make the input parameter a list. Args: an_item (Any): The item to wrap. Returns: - List[Any]: The item wrapped in a list. + list[Any]: The item wrapped in a list. """ if an_item is not None and not isinstance(an_item, list): an_item = [an_item] return an_item -def check_noise_target_gates(noise: Noise, target_gates: Iterable[Type[Gate]]) -> None: +def check_noise_target_gates(noise: Noise, target_gates: Iterable[type[Gate]]) -> None: """Helper function to check 1. whether all the elements in target_gates are a Gate type; 2. if `noise` is multi-qubit noise and `target_gates` contain gates with the number of qubits is the same as `noise.qubit_count`. Args: noise (Noise): A Noise class object to be applied to the circuit. - target_gates (Iterable[Type[Gate]]): Gate class or + target_gates (Iterable[type[Gate]]): Gate class or List of Gate classes which `noise` is applied to. """ if not all(isinstance(g, type) and issubclass(g, Gate) for g in target_gates): @@ -126,7 +127,7 @@ def check_noise_target_qubits( def apply_noise_to_moments( - circuit: Circuit, noise: Iterable[Type[Noise]], target_qubits: QubitSet, position: str + circuit: Circuit, noise: Iterable[type[Noise]], target_qubits: QubitSet, position: str ) -> Circuit: """ Apply initialization/readout noise to the circuit. @@ -138,7 +139,7 @@ def apply_noise_to_moments( Args: circuit (Circuit): A ciruit where `noise` is applied to. - noise (Iterable[Type[Noise]]): Noise channel(s) to be applied + noise (Iterable[type[Noise]]): Noise channel(s) to be applied to the circuit. target_qubits (QubitSet): Index or indices of qubits. `noise` is applied to. position (str): The position to add the noise to. May be 'initialization' or @@ -181,18 +182,18 @@ def apply_noise_to_moments( def _apply_noise_to_gates_helper( - noise: Iterable[Type[Noise]], + noise: Iterable[type[Noise]], target_qubits: QubitSet, instruction: Instruction, noise_index: int, intersection: QubitSet, noise_applied: bool, new_noise_instruction: Iterable, -) -> Tuple[Iterable[Instruction], int, bool]: +) -> tuple[Iterable[Instruction], int, bool]: """Helper function to work out the noise instructions to be attached to a gate. Args: - noise (Iterable[Type[Noise]]): Noise channel(s) to be applied + noise (Iterable[type[Noise]]): Noise channel(s) to be applied to the circuit. target_qubits (QubitSet): Index or indices of qubits which `noise` is applied to. instruction (Instruction): Instruction of the gate which `noise` is applied to. @@ -204,7 +205,7 @@ def _apply_noise_to_gates_helper( to the circuit. Returns: - Tuple[Iterable[Instruction], int, bool]: A tuple of three values: + tuple[Iterable[Instruction], int, bool]: A tuple of three values: new_noise_instruction: A list of noise intructions noise_index: The number of noise channels applied to the gate noise_applied: Whether noise is applied or not @@ -234,8 +235,8 @@ def _apply_noise_to_gates_helper( def apply_noise_to_gates( circuit: Circuit, - noise: Iterable[Type[Noise]], - target_gates: Union[Iterable[Type[Gate]], np.ndarray], + noise: Iterable[type[Noise]], + target_gates: Union[Iterable[type[Gate]], np.ndarray], target_qubits: QubitSet, ) -> Circuit: """Apply noise after target gates in target qubits. @@ -247,9 +248,9 @@ def apply_noise_to_gates( Args: circuit (Circuit): A ciruit where `noise` is applied to. - noise (Iterable[Type[Noise]]): Noise channel(s) to be applied + noise (Iterable[type[Noise]]): Noise channel(s) to be applied to the circuit. - target_gates (Union[Iterable[Type[Gate]], ndarray]): List of gates, or a unitary matrix + target_gates (Union[Iterable[type[Gate]], ndarray]): List of gates, or a unitary matrix which `noise` is applied to. target_qubits (QubitSet): Index or indices of qubits which `noise` is applied to. diff --git a/src/braket/circuits/noise_model/circuit_instruction_criteria.py b/src/braket/circuits/noise_model/circuit_instruction_criteria.py index 0a8c11bdf..170d3e996 100644 --- a/src/braket/circuits/noise_model/circuit_instruction_criteria.py +++ b/src/braket/circuits/noise_model/circuit_instruction_criteria.py @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. from abc import abstractmethod -from typing import Optional, Set, Tuple, Union +from typing import Optional, Union from braket.circuits.instruction import Instruction from braket.circuits.noise_model.criteria import Criteria @@ -36,13 +36,13 @@ def instruction_matches(self, instruction: Instruction) -> bool: @staticmethod def _check_target_in_qubits( - qubits: Optional[Set[Union[int, Tuple[int]]]], target: QubitSetInput + qubits: Optional[set[Union[int, tuple[int]]]], target: QubitSetInput ) -> bool: """ Returns true if the given targets of an instruction match the given qubit input set. Args: - qubits (Optional[Set[Union[int, Tuple[int]]]]): The qubits provided to the criteria. + qubits (Optional[set[Union[int, tuple[int]]]]): The qubits provided to the criteria. target (QubitSetInput): Targets of an instruction. Returns: diff --git a/src/braket/circuits/noise_model/criteria.py b/src/braket/circuits/noise_model/criteria.py index 539bf3829..b9f0d2cc4 100644 --- a/src/braket/circuits/noise_model/criteria.py +++ b/src/braket/circuits/noise_model/criteria.py @@ -14,8 +14,9 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Iterable from enum import Enum -from typing import Any, Iterable, Set, Type, Union +from typing import Any, Union class CriteriaKey(str, Enum): @@ -54,14 +55,14 @@ def applicable_key_types(self) -> Iterable[CriteriaKey]: raise NotImplementedError @abstractmethod - def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, Set[Any]]: + def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, set[Any]]: """Returns a set of key for a given key type. Args: key_type (CriteriaKey): The criteria key type. Returns: - Union[CriteriaKeyResult, Set[Any]]: Returns a set of keys for a key type. The + Union[CriteriaKeyResult, set[Any]]: Returns a set of keys for a key type. The actual returned keys will depend on the CriteriaKey. If the provided key type is not relevant the returned list will be empty. If the provided key type is relevant for all possible inputs, the string CriteriaKeyResult.ALL will be returned. @@ -105,10 +106,10 @@ def from_dict(cls, criteria: dict) -> Criteria: raise NotImplementedError @classmethod - def register_criteria(cls, criteria: Type[Criteria]) -> None: + def register_criteria(cls, criteria: type[Criteria]) -> None: """Register a criteria implementation by adding it into the Criteria class. Args: - criteria (Type[Criteria]): Criteria class to register. + criteria (type[Criteria]): Criteria class to register. """ setattr(cls, criteria.__name__, criteria) diff --git a/src/braket/circuits/noise_model/criteria_input_parsing.py b/src/braket/circuits/noise_model/criteria_input_parsing.py index a47d8d78c..11f24619e 100644 --- a/src/braket/circuits/noise_model/criteria_input_parsing.py +++ b/src/braket/circuits/noise_model/criteria_input_parsing.py @@ -11,7 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Iterable, Optional, Set, Tuple, Union +from collections.abc import Iterable +from typing import Optional, Union from braket.circuits.quantum_operator import QuantumOperator from braket.registers.qubit_set import QubitSetInput @@ -19,7 +20,7 @@ def parse_operator_input( operators: Union[QuantumOperator, Iterable[QuantumOperator]] -) -> Optional[Set[QuantumOperator]]: +) -> Optional[set[QuantumOperator]]: """ Processes the quantum operator input to __init__ to validate and return a set of QuantumOperators. @@ -28,7 +29,7 @@ def parse_operator_input( operators (Union[QuantumOperator, Iterable[QuantumOperator]]): QuantumOperator input. Returns: - Optional[Set[QuantumOperator]]: The set of relevant QuantumOperators or None if none + Optional[set[QuantumOperator]]: The set of relevant QuantumOperators or None if none is specified. Throws: @@ -47,7 +48,7 @@ def parse_operator_input( def parse_qubit_input( qubits: Optional[QubitSetInput], expected_qubit_count: Optional[int] = 0 -) -> Optional[Set[Union[int, Tuple[int]]]]: +) -> Optional[set[Union[int, tuple[int]]]]: """ Processes the qubit input to __init__ to validate and return a set of qubit targets. @@ -58,7 +59,7 @@ def parse_qubit_input( expected qubit count matches the actual qubit count. Default is 0. Returns: - Optional[Set[Union[int, Tuple[int]]]]: The set of qubit targets, or None if no qubits + Optional[set[Union[int, tuple[int]]]]: The set of qubit targets, or None if no qubits are specified. """ if qubits is None: diff --git a/src/braket/circuits/noise_model/gate_criteria.py b/src/braket/circuits/noise_model/gate_criteria.py index 571c6cb61..623c02477 100644 --- a/src/braket/circuits/noise_model/gate_criteria.py +++ b/src/braket/circuits/noise_model/gate_criteria.py @@ -11,7 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Any, Iterable, Optional, Set, Union +from collections.abc import Iterable +from typing import Any, Optional, Union from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction @@ -65,14 +66,14 @@ def applicable_key_types(self) -> Iterable[CriteriaKey]: """ return [CriteriaKey.QUBIT, CriteriaKey.GATE] - def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, Set[Any]]: + def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, set[Any]]: """Gets the keys for a given CriteriaKey. Args: key_type (CriteriaKey): The relevant Criteria Key. Returns: - Union[CriteriaKeyResult, Set[Any]]: The return value is based on the key type: + Union[CriteriaKeyResult, set[Any]]: The return value is based on the key type: GATE will return a set of Gate classes that are relevant to this Criteria. QUBIT will return a set of qubit targets that are relevant to this Criteria, or CriteriaKeyResult.ALL if the Criteria is relevant for all (possible) qubits. diff --git a/src/braket/circuits/noise_model/noise_model.py b/src/braket/circuits/noise_model/noise_model.py index b6805e338..8922cda72 100644 --- a/src/braket/circuits/noise_model/noise_model.py +++ b/src/braket/circuits/noise_model/noise_model.py @@ -15,7 +15,7 @@ from collections import defaultdict from dataclasses import dataclass -from typing import List, Optional, Type +from typing import Optional from braket.circuits.circuit import Circuit from braket.circuits.gate import Gate @@ -76,9 +76,9 @@ def from_dict(cls, noise_model_item: dict) -> NoiseModelInstruction: class NoiseModelInstructions: """Represents the instructions in a noise model, separated by type.""" - initialization_noise: List[NoiseModelInstruction] - gate_noise: List[NoiseModelInstruction] - readout_noise: List[NoiseModelInstruction] + initialization_noise: list[NoiseModelInstruction] + gate_noise: list[NoiseModelInstruction] + readout_noise: list[NoiseModelInstruction] class NoiseModel: @@ -90,7 +90,7 @@ class NoiseModel: a phase flip. """ - def __init__(self, instructions: List[NoiseModelInstruction] = None): + def __init__(self, instructions: list[NoiseModelInstruction] = None): self._instructions = instructions or [] def __repr__(self): @@ -109,12 +109,12 @@ def __str__(self): return "\n".join(all_strings) @property - def instructions(self) -> List[NoiseModelInstruction]: + def instructions(self) -> list[NoiseModelInstruction]: """ List all the noise in the NoiseModel. Returns: - List[NoiseModelInstruction]: The noise model instructions. + list[NoiseModelInstruction]: The noise model instructions. """ return self._instructions @@ -198,7 +198,7 @@ def from_filter( self, qubit: Optional[QubitSetInput] = None, gate: Optional[Gate] = None, - noise: Optional[Type[Noise]] = None, + noise: Optional[type[Noise]] = None, ) -> NoiseModel: """ Returns a new NoiseModel from this NoiseModel using a given filter. If no filters are @@ -211,7 +211,7 @@ def from_filter( gate (Optional[Gate]): The gate to filter. Default is None. If not None, the returned NoiseModel will only have Noise that might be applicable to the passed Gate. - noise (Optional[Type[Noise]]): The noise class to filter. Default is None. + noise (Optional[type[Noise]]): The noise class to filter. Default is None. If not None, the returned NoiseModel will only have noise that is of the same class as the given noise class. @@ -259,7 +259,7 @@ def to_dict(self) -> dict: def _apply_gate_noise( cls, circuit: Circuit, - gate_noise_instructions: List[NoiseModelInstruction], + gate_noise_instructions: list[NoiseModelInstruction], ) -> Circuit: """ Applies the gate noise to return a new circuit that's the `noisy` version of the given @@ -267,7 +267,7 @@ def _apply_gate_noise( Args: circuit (Circuit): a circuit to apply `noise` to. - gate_noise_instructions (List[NoiseModelInstruction]): a list of gate noise + gate_noise_instructions (list[NoiseModelInstruction]): a list of gate noise instructions to apply to the circuit. Returns: @@ -293,14 +293,14 @@ def _apply_gate_noise( def _apply_init_noise( cls, circuit: Circuit, - init_noise_instructions: List[NoiseModelInstruction], + init_noise_instructions: list[NoiseModelInstruction], ) -> Circuit: """ Applies the initialization noise of this noise model to a circuit and returns the circuit. Args: circuit (Circuit): A circuit to apply `noise` to. - init_noise_instructions (List[NoiseModelInstruction]): A list of initialization noise + init_noise_instructions (list[NoiseModelInstruction]): A list of initialization noise model instructions. Returns: @@ -318,14 +318,14 @@ def _apply_init_noise( def _apply_readout_noise( cls, circuit: Circuit, - readout_noise_instructions: List[NoiseModelInstruction], + readout_noise_instructions: list[NoiseModelInstruction], ) -> Circuit: """ Applies the readout noise of this noise model to a circuit and returns the circuit. Args: circuit (Circuit): A circuit to apply `noise` to. - readout_noise_instructions (List[NoiseModelInstruction]): The list of readout noise + readout_noise_instructions (list[NoiseModelInstruction]): The list of readout noise to apply. Returns: @@ -337,17 +337,17 @@ def _apply_readout_noise( @classmethod def _items_to_string( - cls, instructions_title: str, instructions: List[NoiseModelInstruction] - ) -> List[str]: + cls, instructions_title: str, instructions: list[NoiseModelInstruction] + ) -> list[str]: """ Creates a string representation of a list of instructions. Args: instructions_title (str): The title for this list of instructions. - instructions (List[NoiseModelInstruction]): A list of instructions. + instructions (list[NoiseModelInstruction]): A list of instructions. Returns: - List[str]: A list of string representations of the passed instructions. + list[str]: A list of string representations of the passed instructions. """ results = [] if len(instructions) > 0: @@ -376,14 +376,14 @@ def from_dict(cls, noise_dict: dict) -> NoiseModel: def _apply_noise_on_observable_result_types( - circuit: Circuit, readout_noise_instructions: List[NoiseModelInstruction] + circuit: Circuit, readout_noise_instructions: list[NoiseModelInstruction] ) -> Circuit: """Applies readout noise based on the observable result types in the circuit. Each applicable Noise will be applied only once to a target in the ObservableResultType. Args: circuit (Circuit): The circuit to apply the readout noise to. - readout_noise_instructions (List[NoiseModelInstruction]): The list of readout noise + readout_noise_instructions (list[NoiseModelInstruction]): The list of readout noise to apply. Returns: diff --git a/src/braket/circuits/noise_model/observable_criteria.py b/src/braket/circuits/noise_model/observable_criteria.py index 46c0e3e7c..2275ccce7 100644 --- a/src/braket/circuits/noise_model/observable_criteria.py +++ b/src/braket/circuits/noise_model/observable_criteria.py @@ -11,7 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Any, Iterable, Optional, Set, Union +from collections.abc import Iterable +from typing import Any, Optional, Union from braket.circuits.noise_model.criteria import Criteria, CriteriaKey, CriteriaKeyResult from braket.circuits.noise_model.criteria_input_parsing import ( @@ -71,14 +72,14 @@ def applicable_key_types(self) -> Iterable[CriteriaKey]: """ return [CriteriaKey.OBSERVABLE, CriteriaKey.QUBIT] - def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, Set[Any]]: + def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, set[Any]]: """Gets the keys for a given CriteriaKey. Args: key_type (CriteriaKey): The relevant Criteria Key. Returns: - Union[CriteriaKeyResult, Set[Any]]: The return value is based on the key type: + Union[CriteriaKeyResult, set[Any]]: The return value is based on the key type: OBSERVABLE will return a set of Observable classes that are relevant to this Criteria, or CriteriaKeyResult.ALL if the Criteria is relevant for all (possible) observables. QUBIT will return a set of qubit targets that are relevant to this Criteria, or diff --git a/src/braket/circuits/noise_model/qubit_initialization_criteria.py b/src/braket/circuits/noise_model/qubit_initialization_criteria.py index dade3a0e4..abed13af8 100644 --- a/src/braket/circuits/noise_model/qubit_initialization_criteria.py +++ b/src/braket/circuits/noise_model/qubit_initialization_criteria.py @@ -11,7 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Any, Iterable, Optional, Set, Union +from collections.abc import Iterable +from typing import Any, Optional, Union from braket.circuits.noise_model.criteria import Criteria, CriteriaKey, CriteriaKeyResult from braket.circuits.noise_model.criteria_input_parsing import parse_qubit_input @@ -45,14 +46,14 @@ def applicable_key_types(self) -> Iterable[CriteriaKey]: """ return [CriteriaKey.QUBIT] - def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, Set[Any]]: + def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, set[Any]]: """Gets the keys for a given CriteriaKey. Args: key_type (CriteriaKey): The relevant Criteria Key. Returns: - Union[CriteriaKeyResult, Set[Any]]: The return value is based on the key type: + Union[CriteriaKeyResult, set[Any]]: The return value is based on the key type: QUBIT will return a set of qubit targets that are relevant to this Critera, or CriteriaKeyResult.ALL if the Criteria is relevant for all (possible) qubits. All other keys will return an empty set. diff --git a/src/braket/circuits/noise_model/unitary_gate_criteria.py b/src/braket/circuits/noise_model/unitary_gate_criteria.py index 7642a143e..229fc11ba 100644 --- a/src/braket/circuits/noise_model/unitary_gate_criteria.py +++ b/src/braket/circuits/noise_model/unitary_gate_criteria.py @@ -11,7 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Any, Iterable, Optional, Set, Union +from collections.abc import Iterable +from typing import Any, Optional, Union from braket.circuits.gates import Unitary from braket.circuits.instruction import Instruction @@ -53,14 +54,14 @@ def applicable_key_types(self) -> Iterable[CriteriaKey]: """ return [CriteriaKey.QUBIT, CriteriaKey.UNITARY_GATE] - def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, Set[Any]]: + def get_keys(self, key_type: CriteriaKey) -> Union[CriteriaKeyResult, set[Any]]: """Gets the keys for a given CriteriaKey. Args: key_type (CriteriaKey): The relevant Criteria Key. Returns: - Union[CriteriaKeyResult, Set[Any]]: The return value is based on the key type: + Union[CriteriaKeyResult, set[Any]]: The return value is based on the key type: UNITARY_GATE will return a set containing the bytes of the unitary matrix representing the unitary gate. QUBIT will return a set of qubit targets that are relevant to this Criteria, or diff --git a/src/braket/circuits/noises.py b/src/braket/circuits/noises.py index 11dac20e8..572489a9d 100644 --- a/src/braket/circuits/noises.py +++ b/src/braket/circuits/noises.py @@ -12,7 +12,8 @@ # language governing permissions and limitations under the License. import itertools -from typing import Any, Dict, Iterable, List, Union +from collections.abc import Iterable +from typing import Any, Union import numpy as np @@ -357,7 +358,7 @@ def pauli_channel( Args: target (QubitSetInput): Target qubit(s) - probability List[float]: Probabilities for the Pauli X, Y and Z noise + probability list[float]: Probabilities for the Pauli X, Y and Z noise happening in the Kraus channel. probX (float): X rotation probability. probY (float): Y rotation probability. @@ -863,7 +864,7 @@ class TwoQubitPauliChannel(MultiQubitPauliNoise): _tensor_products_strings = itertools.product(_paulis.keys(), repeat=2) _names_list = ["".join(x) for x in _tensor_products_strings] - def __init__(self, probabilities: Dict[str, float]): + def __init__(self, probabilities: dict[str, float]): super().__init__( probabilities=probabilities, qubit_count=None, @@ -906,14 +907,14 @@ def fixed_qubit_count() -> int: @staticmethod @circuit.subroutine(register=True) def two_qubit_pauli_channel( - target1: QubitInput, target2: QubitInput, probabilities: Dict[str, float] + target1: QubitInput, target2: QubitInput, probabilities: dict[str, float] ) -> Iterable[Instruction]: """Registers this function into the circuit class. Args: target1 (QubitInput): Target qubit 1. target2 (QubitInput): Target qubit 2. - probabilities (Dict[str, float]): Probability of two-qubit Pauli channel. + probabilities (dict[str, float]): Probability of two-qubit Pauli channel. Returns: Iterable[Instruction]: `Iterable` of Depolarizing instructions. @@ -1374,7 +1375,7 @@ def _to_openqasm( return f"#pragma braket noise kraus({matrix_list}) {qubit_list}" @staticmethod - def _transform_matrix_to_ir(matrices: Iterable[np.ndarray]) -> List: + def _transform_matrix_to_ir(matrices: Iterable[np.ndarray]) -> list: serializable = [] for matrix in matrices: matrix_as_list = [ @@ -1439,14 +1440,14 @@ def from_dict(cls, noise: dict) -> Noise: def _ascii_representation( - noise: str, parameters: List[Union[FreeParameterExpression, float]] + noise: str, parameters: list[Union[FreeParameterExpression, float]] ) -> str: """ Generates a formatted ascii representation of a noise. Args: noise (str): The name of the noise. - parameters (List[Union[FreeParameterExpression, float]]): The parameters to the noise. + parameters (list[Union[FreeParameterExpression, float]]): The parameters to the noise. Returns: str: The ascii representation of the noise. diff --git a/src/braket/circuits/observable.py b/src/braket/circuits/observable.py index 6b70d3384..217bd5972 100644 --- a/src/braket/circuits/observable.py +++ b/src/braket/circuits/observable.py @@ -14,8 +14,9 @@ from __future__ import annotations import numbers +from collections.abc import Sequence from copy import deepcopy -from typing import List, Sequence, Tuple, Union +from typing import Union import numpy as np @@ -49,7 +50,7 @@ def to_ir( target: QubitSet = None, ir_type: IRType = IRType.JAQCD, serialization_properties: SerializationProperties = None, - ) -> Union[str, List[Union[str, List[List[List[float]]]]]]: + ) -> Union[str, list[Union[str, list[list[list[float]]]]]]: """Returns the IR representation for the observable Args: @@ -61,7 +62,7 @@ def to_ir( supplied must correspond to the supplied `ir_type`. Defaults to None. Returns: - Union[str, List[Union[str, List[List[List[float]]]]]]: The IR representation for + Union[str, list[Union[str, list[list[list[float]]]]]]: The IR representation for the observable. Raises: @@ -84,7 +85,7 @@ def to_ir( else: raise ValueError(f"Supplied ir_type {ir_type} is not supported.") - def _to_jaqcd(self) -> List[Union[str, List[List[List[float]]]]]: + def _to_jaqcd(self) -> list[Union[str, list[list[list[float]]]]]: """Returns the JAQCD representation of the observable.""" raise NotImplementedError("to_jaqcd has not been implemented yet.") @@ -113,10 +114,10 @@ def coefficient(self) -> int: return self._coef @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: """Returns the basis rotation gates for this observable. Returns: - Tuple[Gate, ...]: The basis rotation gates for this observable. + tuple[Gate, ...]: The basis rotation gates for this observable. """ raise NotImplementedError @@ -210,7 +211,7 @@ def eigenvalue(self, index: int) -> float: return self.coefficient * self._eigenvalues[index] @property - def ascii_symbols(self) -> Tuple[str, ...]: + def ascii_symbols(self) -> tuple[str, ...]: return tuple( f"{self.coefficient if self.coefficient != 1 else ''}{ascii_symbol}" for ascii_symbol in self._ascii_symbols diff --git a/src/braket/circuits/observables.py b/src/braket/circuits/observables.py index 9b1a4904b..dc0ff0782 100644 --- a/src/braket/circuits/observables.py +++ b/src/braket/circuits/observables.py @@ -18,7 +18,7 @@ import math import numbers from copy import deepcopy -from typing import Dict, List, Tuple, Union +from typing import Union import numpy as np @@ -46,7 +46,7 @@ def __init__(self): def _unscaled(self) -> StandardObservable: return H() - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return ["h"] @@ -67,7 +67,7 @@ def to_matrix(self) -> np.ndarray: ) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return tuple([Gate.Ry(-math.pi / 4)]) @@ -87,7 +87,7 @@ def __init__(self): def _unscaled(self) -> Observable: return I() - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return ["i"] @@ -106,7 +106,7 @@ def to_matrix(self) -> np.ndarray: return self.coefficient * np.eye(2, dtype=complex) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return () @property @@ -137,7 +137,7 @@ def __init__(self): def _unscaled(self) -> StandardObservable: return X() - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return ["x"] @@ -156,7 +156,7 @@ def to_matrix(self) -> np.ndarray: return self.coefficient * np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return tuple([Gate.H()]) @@ -176,7 +176,7 @@ def __init__(self): def _unscaled(self) -> StandardObservable: return Y() - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return ["y"] @@ -195,7 +195,7 @@ def to_matrix(self) -> np.ndarray: return self.coefficient * np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return tuple([Gate.Z(), Gate.S(), Gate.H()]) @@ -215,7 +215,7 @@ def __init__(self): def _unscaled(self) -> StandardObservable: return Z() - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return ["z"] @@ -234,7 +234,7 @@ def to_matrix(self) -> np.ndarray: return self.coefficient * np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return () @@ -244,10 +244,10 @@ def basis_rotation_gates(self) -> Tuple[Gate, ...]: class TensorProduct(Observable): """Tensor product of observables""" - def __init__(self, observables: List[Observable]): + def __init__(self, observables: list[Observable]): """ Args: - observables (List[Observable]): List of observables for tensor product + observables (list[Observable]): List of observables for tensor product Examples: >>> t1 = Observable.Y() @ Observable.X() @@ -295,7 +295,7 @@ def __init__(self, observables: List[Observable]): self._all_eigenvalues = None @property - def ascii_symbols(self) -> Tuple[str, ...]: + def ascii_symbols(self) -> tuple[str, ...]: return tuple( f"{self.coefficient if self.coefficient != 1 else ''}" f"{'@'.join([obs.ascii_symbols[0] for obs in self.factors])}" @@ -307,7 +307,7 @@ def _unscaled(self) -> Observable: copied._coef = 1 return copied - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") ir = [] @@ -336,8 +336,8 @@ def _to_openqasm( return f"{coef_prefix}{' @ '.join(factors)}" @property - def factors(self) -> Tuple[Observable, ...]: - """Tuple[Observable]: The observables that comprise this tensor product.""" + def factors(self) -> tuple[Observable, ...]: + """tuple[Observable]: The observables that comprise this tensor product.""" return self._factors def to_matrix(self) -> np.ndarray: @@ -346,10 +346,10 @@ def to_matrix(self) -> np.ndarray: ) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: """Returns the basis rotation gates for this observable. Returns: - Tuple[Gate, ...]: The basis rotation gates for this observable. + tuple[Gate, ...]: The basis rotation gates for this observable. """ gates = [] for obs in self.factors: @@ -404,7 +404,7 @@ def __eq__(self, other): return self.matrix_equivalence(other) @staticmethod - def _compute_eigenvalues(observables: Tuple[Observable], num_qubits: int) -> np.ndarray: + def _compute_eigenvalues(observables: tuple[Observable], num_qubits: int) -> np.ndarray: if False in [isinstance(observable, StandardObservable) for observable in observables]: # Tensor product of observables contains a mixture # of standard and non-standard observables @@ -431,10 +431,10 @@ def _compute_eigenvalues(observables: Tuple[Observable], num_qubits: int) -> np. class Sum(Observable): """Sum of observables""" - def __init__(self, observables: List[Observable], display_name: str = "Hamiltonian"): + def __init__(self, observables: list[Observable], display_name: str = "Hamiltonian"): """ Args: - observables (List[Observable]): List of observables for Sum + observables (list[Observable]): List of observables for Sum display_name (str): Name to use for an instance of this Sum observable for circuit diagrams. Defaults to `Hamiltonian`. @@ -465,13 +465,13 @@ def __mul__(self, other) -> Observable: return sum_copy raise TypeError("Observable coefficients must be numbers.") - def _to_jaqcd(self) -> List[str]: + def _to_jaqcd(self) -> list[str]: raise NotImplementedError("Sum Observable is not supported in Jaqcd") def _to_openqasm( self, serialization_properties: OpenQASMSerializationProperties, - target: List[QubitSet] = None, + target: list[QubitSet] = None, ) -> str: if len(self.summands) != len(target): raise ValueError( @@ -493,15 +493,15 @@ def _to_openqasm( ).replace("+ -", "- ") @property - def summands(self) -> Tuple[Observable, ...]: - """Tuple[Observable]: The observables that comprise this sum.""" + def summands(self) -> tuple[Observable, ...]: + """tuple[Observable]: The observables that comprise this sum.""" return self._summands def to_matrix(self) -> np.ndarray: raise NotImplementedError("Matrix operation is not supported for Sum") @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: raise NotImplementedError("Basis rotation calculation not supported for Sum") @property @@ -518,7 +518,7 @@ def __eq__(self, other): return repr(self) == repr(other) @staticmethod - def _compute_eigenvalues(observables: Tuple[Observable], num_qubits: int) -> np.ndarray: + def _compute_eigenvalues(observables: tuple[Observable], num_qubits: int) -> np.ndarray: raise NotImplementedError("Eigenvalue calculation not supported for Sum") @@ -563,7 +563,7 @@ def __init__(self, matrix: np.ndarray, display_name: str = "Hermitian"): def _unscaled(self) -> Observable: return Hermitian(matrix=self._matrix, display_name=self.ascii_symbols[0]) - def _to_jaqcd(self) -> List[List[List[List[float]]]]: + def _to_jaqcd(self) -> list[list[list[list[float]]]]: if self.coefficient != 1: raise ValueError("Observable coefficients not supported with Jaqcd") return [ @@ -598,7 +598,7 @@ def __eq__(self, other) -> bool: return self.matrix_equivalence(other) @property - def basis_rotation_gates(self) -> Tuple[Gate, ...]: + def basis_rotation_gates(self) -> tuple[Gate, ...]: return self._diagonalizing_gates @property @@ -613,7 +613,7 @@ def eigenvalue(self, index: int) -> float: return self._eigenvalues[index] @staticmethod - def _get_eigendecomposition(matrix: np.ndarray) -> Dict[str, np.ndarray]: + def _get_eigendecomposition(matrix: np.ndarray) -> dict[str, np.ndarray]: """ Decomposes the Hermitian matrix into its eigenvectors and associated eigenvalues. The eigendecomposition is cached so that if another Hermitian observable @@ -624,7 +624,7 @@ def _get_eigendecomposition(matrix: np.ndarray) -> Dict[str, np.ndarray]: matrix (ndarray): The Hermitian matrix. Returns: - Dict[str, ndarray]: The keys are "eigenvectors_conj_t", mapping to the + dict[str, ndarray]: The keys are "eigenvectors_conj_t", mapping to the conjugate transpose of a matrix whose columns are the eigenvectors of the matrix, and "eigenvalues", a list of associated eigenvalues in the order of their corresponding eigenvectors in the "eigenvectors" matrix. These cached values @@ -648,13 +648,13 @@ def __repr__(self): Observable.register_observable(Hermitian) -def observable_from_ir(ir_observable: List[Union[str, List[List[List[float]]]]]) -> Observable: +def observable_from_ir(ir_observable: list[Union[str, list[list[list[float]]]]]) -> Observable: """ Create an observable from the IR observable list. This can be a tensor product of observables or a single observable. Args: - ir_observable (List[Union[str, List[List[List[float]]]]]): observable as defined in IR + ir_observable (list[Union[str, list[list[list[float]]]]]): observable as defined in IR Returns: Observable: observable object @@ -666,7 +666,7 @@ def observable_from_ir(ir_observable: List[Union[str, List[List[List[float]]]]]) return observable -def _observable_from_ir_list_item(observable: Union[str, List[List[List[float]]]]) -> Observable: +def _observable_from_ir_list_item(observable: Union[str, list[list[list[float]]]]) -> Observable: if observable == "i": return I() elif observable == "h": diff --git a/src/braket/circuits/quantum_operator.py b/src/braket/circuits/quantum_operator.py index 846573c67..df67bf199 100644 --- a/src/braket/circuits/quantum_operator.py +++ b/src/braket/circuits/quantum_operator.py @@ -13,7 +13,8 @@ from __future__ import annotations -from typing import Any, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import Any, Optional import numpy as np @@ -96,8 +97,8 @@ def qubit_count(self) -> int: return self._qubit_count @property - def ascii_symbols(self) -> Tuple[str, ...]: - """Tuple[str, ...]: Returns the ascii symbols for the quantum operator.""" + def ascii_symbols(self) -> tuple[str, ...]: + """tuple[str, ...]: Returns the ascii symbols for the quantum operator.""" return self._ascii_symbols @property diff --git a/src/braket/circuits/quantum_operator_helpers.py b/src/braket/circuits/quantum_operator_helpers.py index cb6da9e80..a264d0b38 100644 --- a/src/braket/circuits/quantum_operator_helpers.py +++ b/src/braket/circuits/quantum_operator_helpers.py @@ -11,8 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. +from collections.abc import Iterable from functools import lru_cache -from typing import Iterable import numpy as np diff --git a/src/braket/circuits/result_type.py b/src/braket/circuits/result_type.py index 787d9ee0d..a59d9ff3d 100644 --- a/src/braket/circuits/result_type.py +++ b/src/braket/circuits/result_type.py @@ -13,7 +13,7 @@ from __future__ import annotations -from typing import Any, Dict, List, Type, Union +from typing import Any, Union from braket.circuits.free_parameter import FreeParameter from braket.circuits.observable import Observable @@ -34,10 +34,10 @@ class ResultType: the metadata that defines what a requested result type is and what it does. """ - def __init__(self, ascii_symbols: List[str]): + def __init__(self, ascii_symbols: list[str]): """ Args: - ascii_symbols (List[str]): ASCII string symbols for the result type. This is used when + ascii_symbols (list[str]): ASCII string symbols for the result type. This is used when printing a diagram of circuits. Raises: @@ -50,8 +50,8 @@ def __init__(self, ascii_symbols: List[str]): self._ascii_symbols = ascii_symbols @property - def ascii_symbols(self) -> List[str]: - """List[str]: Returns the ascii symbols for the requested result type.""" + def ascii_symbols(self) -> list[str]: + """list[str]: Returns the ascii symbols for the requested result type.""" return self._ascii_symbols @property @@ -118,7 +118,7 @@ def _to_openqasm(self, serialization_properties: OpenQASMSerializationProperties raise NotImplementedError("to_openqasm has not been implemented yet.") def copy( - self, target_mapping: Dict[QubitInput, QubitInput] = None, target: QubitSetInput = None + self, target_mapping: dict[QubitInput, QubitInput] = None, target: QubitSetInput = None ) -> ResultType: """ Return a shallow copy of the result type. @@ -128,7 +128,7 @@ def copy( qubits. This is useful apply an instruction to a circuit and change the target qubits. Args: - target_mapping (Dict[QubitInput, QubitInput]): A dictionary of + target_mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply to the target. Key is the qubit in this `target` and the value is what the key is changed to. Default = `None`. target (QubitSetInput): Target qubits for the new instruction. @@ -162,11 +162,11 @@ def copy( return copy @classmethod - def register_result_type(cls, result_type: Type[ResultType]) -> None: + def register_result_type(cls, result_type: type[ResultType]) -> None: """Register a result type implementation by adding it into the `ResultType` class. Args: - result_type (Type[ResultType]): `ResultType` class to register. + result_type (type[ResultType]): `ResultType` class to register. """ setattr(cls, result_type.__name__, result_type) @@ -188,11 +188,11 @@ class ObservableResultType(ResultType): """ def __init__( - self, ascii_symbols: List[str], observable: Observable, target: QubitSetInput = None + self, ascii_symbols: list[str], observable: Observable, target: QubitSetInput = None ): """ Args: - ascii_symbols (List[str]): ASCII string symbols for the result type. This is used when + ascii_symbols (list[str]): ASCII string symbols for the result type. This is used when printing a diagram of circuits. observable (Observable): the observable for the result type target (QubitSetInput): Target qubits that the @@ -285,10 +285,10 @@ class ObservableParameterResultType(ObservableResultType): def __init__( self, - ascii_symbols: List[str], + ascii_symbols: list[str], observable: Observable, target: QubitSetInput = None, - parameters: List[Union[str, FreeParameter]] = None, + parameters: list[Union[str, FreeParameter]] = None, ): super().__init__(ascii_symbols, observable, target) @@ -300,13 +300,13 @@ def __init__( """ Args: - ascii_symbols (List[str]): ASCII string symbols for the result type. This is used when + ascii_symbols (list[str]): ASCII string symbols for the result type. This is used when printing a diagram of circuits. observable (Observable): the observable for the result type. target (QubitSetInput): Target qubits that the result type is requested for. Default is `None`, which means the observable must only operate on 1 qubit and it will be applied to all qubits in parallel. - parameters (List[Union[str, FreeParameter]]): List of string inputs or + parameters (list[Union[str, FreeParameter]]): List of string inputs or FreeParameter objects. These inputs will be used as parameters for gradient calculation. Default: `all`. @@ -318,7 +318,7 @@ def __init__( """ @property - def parameters(self) -> List[str]: + def parameters(self) -> list[str]: return self._parameters def __repr__(self) -> str: diff --git a/src/braket/circuits/result_types.py b/src/braket/circuits/result_types.py index 25ff63716..cfe216164 100644 --- a/src/braket/circuits/result_types.py +++ b/src/braket/circuits/result_types.py @@ -15,7 +15,7 @@ import re from functools import reduce -from typing import List, Union +from typing import Union import braket.ir.jaqcd as ir from braket.circuits import circuit @@ -178,19 +178,19 @@ class AdjointGradient(ObservableParameterResultType): def __init__( self, observable: Observable, - target: List[QubitSetInput] = None, - parameters: List[Union[str, FreeParameter]] = None, + target: list[QubitSetInput] = None, + parameters: list[Union[str, FreeParameter]] = None, ): """ Args: observable (Observable): The expectation value of this observable is the function against which parameters in the gradient are differentiated. - target (List[QubitSetInput]): Target qubits that the result type is requested for. + target (list[QubitSetInput]): Target qubits that the result type is requested for. Each term in the target list should have the same number of qubits as the corresponding term in the observable. Default is `None`, which means the observable must operate only on 1 qubit and it is applied to all qubits in parallel. - parameters (List[Union[str, FreeParameter]]): The free parameters in the circuit to + parameters (list[Union[str, FreeParameter]]): The free parameters in the circuit to differentiate with respect to. Default: `all`. Raises: @@ -240,20 +240,20 @@ def _to_openqasm(self, serialization_properties: OpenQASMSerializationProperties @circuit.subroutine(register=True) def adjoint_gradient( observable: Observable, - target: List[QubitSetInput] = None, - parameters: List[Union[str, FreeParameter]] = None, + target: list[QubitSetInput] = None, + parameters: list[Union[str, FreeParameter]] = None, ) -> ResultType: """Registers this function into the circuit class. Args: observable (Observable): The expectation value of this observable is the function against which parameters in the gradient are differentiated. - target (List[QubitSetInput]): Target qubits that the result type is requested for. + target (list[QubitSetInput]): Target qubits that the result type is requested for. Each term in the target list should have the same number of qubits as the corresponding term in the observable. Default is `None`, which means the observable must operate only on 1 qubit and it is applied to all qubits in parallel. - parameters (List[Union[str, FreeParameter]]): The free parameters in the circuit to + parameters (list[Union[str, FreeParameter]]): The free parameters in the circuit to differentiate with respect to. Default: `all`. Returns: @@ -279,10 +279,10 @@ class Amplitude(ResultType): This is available on simulators only when `shots=0`. """ - def __init__(self, state: List[str]): + def __init__(self, state: list[str]): """ Args: - state (List[str]): list of quantum states as strings with "0" and "1" + state (list[str]): list of quantum states as strings with "0" and "1" Raises: ValueError: If state is `None` or an empty list, or @@ -293,7 +293,7 @@ def __init__(self, state: List[str]): """ if ( not state - or not isinstance(state, List) + or not isinstance(state, list) or not all( isinstance(amplitude, str) and re.fullmatch("^[01]+$", amplitude) for amplitude in state @@ -306,7 +306,7 @@ def __init__(self, state: List[str]): self._state = state @property - def state(self) -> List[str]: + def state(self) -> list[str]: return self._state def _to_jaqcd(self) -> ir.Amplitude: @@ -318,11 +318,11 @@ def _to_openqasm(self, serialization_properties: OpenQASMSerializationProperties @staticmethod @circuit.subroutine(register=True) - def amplitude(state: List[str]) -> ResultType: + def amplitude(state: list[str]) -> ResultType: """Registers this function into the circuit class. Args: - state (List[str]): list of quantum states as strings with "0" and "1" + state (list[str]): list of quantum states as strings with "0" and "1" Returns: ResultType: state vector as a requested result type diff --git a/src/braket/circuits/unitary_calculation.py b/src/braket/circuits/unitary_calculation.py index 2059976e9..d51a378cf 100644 --- a/src/braket/circuits/unitary_calculation.py +++ b/src/braket/circuits/unitary_calculation.py @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Iterable +from collections.abc import Iterable import numpy as np from scipy.linalg import fractional_matrix_power diff --git a/src/braket/devices/device.py b/src/braket/devices/device.py index 6ecb2ecc2..49f510edf 100644 --- a/src/braket/devices/device.py +++ b/src/braket/devices/device.py @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Union +from typing import Optional, Union from braket.annealing.problem import Problem from braket.circuits import Circuit @@ -37,7 +37,7 @@ def run( self, task_specification: Union[Circuit, Problem], shots: Optional[int], - inputs: Optional[Dict[str, float]], + inputs: Optional[dict[str, float]], *args, **kwargs ) -> QuantumTask: @@ -49,7 +49,7 @@ def run( to run on device. shots (Optional[int]): The number of times to run the quantum task on the device. Default is `None`. - inputs (Optional[Dict[str, float]]): Inputs to be passed along with the + inputs (Optional[dict[str, float]]): Inputs to be passed along with the IR. If IR is an OpenQASM Program, the inputs will be updated with this value. Not all devices and IR formats support inputs. Default: {}. @@ -62,24 +62,24 @@ def run_batch( self, task_specifications: Union[ Union[Circuit, Problem], - List[Union[Circuit, Problem]], + list[Union[Circuit, Problem]], ], shots: Optional[int], max_parallel: Optional[int], - inputs: Optional[Union[Dict[str, float], List[Dict[str, float]]]], + inputs: Optional[Union[dict[str, float], list[dict[str, float]]]], *args, **kwargs ) -> QuantumTaskBatch: """Executes a batch of quantum tasks in parallel Args: - task_specifications (Union[Union[Circuit, Problem], List[Union[Circuit, Problem]]]): + task_specifications (Union[Union[Circuit, Problem], list[Union[Circuit, Problem]]]): Single instance or list of circuits or problems to run on device. shots (Optional[int]): The number of times to run the circuit or annealing problem. max_parallel (Optional[int]): The maximum number of quantum tasks to run in parallel. Batch creation will fail if this value is greater than the maximum allowed concurrent quantum tasks on the device. - inputs (Optional[Union[Dict[str, float], List[Dict[str, float]]]]): Inputs to be + inputs (Optional[Union[dict[str, float], list[dict[str, float]]]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. diff --git a/src/braket/devices/local_simulator.py b/src/braket/devices/local_simulator.py index a4462ab65..c719978ac 100644 --- a/src/braket/devices/local_simulator.py +++ b/src/braket/devices/local_simulator.py @@ -17,7 +17,7 @@ from itertools import repeat from multiprocessing import Pool from os import cpu_count -from typing import Dict, List, Optional, Set, Union +from typing import Optional, Union import pkg_resources @@ -68,7 +68,7 @@ def run( self, task_specification: Union[Circuit, Problem, Program, AnalogHamiltonianSimulation], shots: int = 0, - inputs: Optional[Dict[str, float]] = None, + inputs: Optional[dict[str, float]] = None, *args, **kwargs, ) -> LocalQuantumTask: @@ -81,7 +81,7 @@ def run( Default is 0, which means that the simulator will compute the exact results based on the quantum task specification. Sampling is not supported for shots=0. - inputs (Optional[Dict[str, float]]): Inputs to be passed along with the + inputs (Optional[dict[str, float]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. @@ -105,24 +105,24 @@ def run_batch( self, task_specifications: Union[ Union[Circuit, Problem, Program, AnalogHamiltonianSimulation], - List[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation]], + list[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation]], ], shots: Optional[int] = 0, max_parallel: Optional[int] = None, - inputs: Optional[Union[Dict[str, float], List[Dict[str, float]]]] = None, + inputs: Optional[Union[dict[str, float], list[dict[str, float]]]] = None, *args, **kwargs, ) -> LocalQuantumTaskBatch: """Executes a batch of quantum tasks in parallel Args: - task_specifications (Union[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation], List[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation]]]): # noqa + task_specifications (Union[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation], list[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation]]]): # noqa Single instance or list of quantum task specification. shots (Optional[int]): The number of times to run the quantum task. Default: 0. max_parallel (Optional[int]): The maximum number of quantum tasks to run in parallel. Default is the number of CPU. - inputs (Optional[Union[Dict[str, float], List[Dict[str, float]]]]): Inputs to be passed + inputs (Optional[Union[dict[str, float], list[dict[str, float]]]]): Inputs to be passed along with the IR. If the IR supports inputs, the inputs will be updated with this value. Default: {}. @@ -188,11 +188,11 @@ def properties(self) -> DeviceCapabilities: return self._delegate.properties @staticmethod - def registered_backends() -> Set[str]: + def registered_backends() -> set[str]: """Gets the backends that have been registered as entry points Returns: - Set[str]: The names of the available backends that can be passed + set[str]: The names of the available backends that can be passed into LocalSimulator's constructor """ return set(_simulator_devices.keys()) @@ -201,7 +201,7 @@ def _run_internal_wrap( self, task_specification: Union[Circuit, Problem, Program, AnalogHamiltonianSimulation], shots: Optional[int] = None, - inputs: Optional[Dict[str, float]] = None, + inputs: Optional[dict[str, float]] = None, *args, **kwargs, ) -> Union[GateModelQuantumTaskResult, AnnealingQuantumTaskResult]: # pragma: no cover @@ -243,7 +243,7 @@ def _( self, circuit: Circuit, shots: Optional[int] = None, - inputs: Optional[Dict[str, float]] = None, + inputs: Optional[dict[str, float]] = None, *args, **kwargs, ): @@ -278,7 +278,7 @@ def _( self, program: Program, shots: Optional[int] = None, - inputs: Optional[Dict[str, float]] = None, + inputs: Optional[dict[str, float]] = None, *args, **kwargs, ): diff --git a/src/braket/error_mitigation/debias.py b/src/braket/error_mitigation/debias.py index 154094a06..305bf7b78 100644 --- a/src/braket/error_mitigation/debias.py +++ b/src/braket/error_mitigation/debias.py @@ -11,8 +11,6 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import List - from braket.device_schema import error_mitigation from braket.error_mitigation.error_mitigation import ErrorMitigation @@ -22,5 +20,5 @@ class Debias(ErrorMitigation): The debias error mitigation scheme. This scheme takes no parameters. """ - def serialize(self) -> List[error_mitigation.Debias]: + def serialize(self) -> list[error_mitigation.Debias]: return [error_mitigation.Debias()] diff --git a/src/braket/error_mitigation/error_mitigation.py b/src/braket/error_mitigation/error_mitigation.py index c03fbf6df..79b1f3e30 100644 --- a/src/braket/error_mitigation/error_mitigation.py +++ b/src/braket/error_mitigation/error_mitigation.py @@ -11,16 +11,14 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import List - from braket.device_schema import error_mitigation class ErrorMitigation: - def serialize(self) -> List[error_mitigation.ErrorMitigationScheme]: + def serialize(self) -> list[error_mitigation.ErrorMitigationScheme]: """ Returns: - List[ErrorMitigationScheme]: A list of service-readable error + list[ErrorMitigationScheme]: A list of service-readable error mitigation scheme descriptions. """ raise NotImplementedError("serialize is not implemented.") diff --git a/src/braket/jobs/data_persistence.py b/src/braket/jobs/data_persistence.py index aa574d0d9..10806812f 100644 --- a/src/braket/jobs/data_persistence.py +++ b/src/braket/jobs/data_persistence.py @@ -126,7 +126,7 @@ def load_job_result(filename: Union[str, Path] = None) -> Dict[str, Any]: must be in the format used by `save_job_result`. Returns: - Dict[str, Any]: Job result data of current job + Dict[str, Any]: Job result data of current job """ persisted_data = _load_persisted_data(filename) deserialized_data = deserialize_values(persisted_data.dataDictionary, persisted_data.dataFormat) diff --git a/src/braket/parametric/free_parameter.py b/src/braket/parametric/free_parameter.py index 3eed47efe..db22f5f60 100644 --- a/src/braket/parametric/free_parameter.py +++ b/src/braket/parametric/free_parameter.py @@ -14,7 +14,7 @@ from __future__ import annotations from numbers import Number -from typing import Dict, Union +from typing import Union from sympy import Symbol @@ -59,12 +59,12 @@ def name(self) -> str: """ return self._name.name - def subs(self, parameter_values: Dict[str, Number]) -> Union[FreeParameter, Number]: + def subs(self, parameter_values: dict[str, Number]) -> Union[FreeParameter, Number]: """ Substitutes a value in if the parameter exists within the mapping. Args: - parameter_values (Dict[str, Number]): A mapping of parameter to its + parameter_values (dict[str, Number]): A mapping of parameter to its corresponding value. Returns: diff --git a/src/braket/parametric/free_parameter_expression.py b/src/braket/parametric/free_parameter_expression.py index 1b64e2ddb..cd5fd7f89 100644 --- a/src/braket/parametric/free_parameter_expression.py +++ b/src/braket/parametric/free_parameter_expression.py @@ -15,7 +15,7 @@ import ast from numbers import Number -from typing import Any, Dict, Union +from typing import Any, Union from sympy import Expr, Float, Symbol, sympify @@ -69,14 +69,14 @@ def expression(self) -> Union[Number, Expr]: return self._expression def subs( - self, parameter_values: Dict[str, Number] + self, parameter_values: dict[str, Number] ) -> Union[FreeParameterExpression, Number, Expr]: """ Similar to a substitution in Sympy. Parameters are swapped for corresponding values or expressions from the dictionary. Args: - parameter_values (Dict[str, Number]): A mapping of parameters to their corresponding + parameter_values (dict[str, Number]): A mapping of parameters to their corresponding values to be assigned. Returns: diff --git a/src/braket/parametric/parameterizable.py b/src/braket/parametric/parameterizable.py index 45c7561ff..bd5dbc5a7 100644 --- a/src/braket/parametric/parameterizable.py +++ b/src/braket/parametric/parameterizable.py @@ -14,7 +14,7 @@ from __future__ import annotations from abc import ABC, abstractmethod -from typing import Any, List, Union +from typing import Any, Union from braket.parametric.free_parameter import FreeParameter from braket.parametric.free_parameter_expression import FreeParameterExpression @@ -28,11 +28,11 @@ class Parameterizable(ABC): @property @abstractmethod - def parameters(self) -> List[Union[FreeParameterExpression, FreeParameter, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, FreeParameter, float]]: """Get the parameters. Returns: - List[Union[FreeParameterExpression, FreeParameter, float]]: The parameters associated + list[Union[FreeParameterExpression, FreeParameter, float]]: The parameters associated with the object, either unbound free parameter expressions or bound values. The order of the parameters is determined by the subclass. """ diff --git a/src/braket/pulse/ast/approximation_parser.py b/src/braket/pulse/ast/approximation_parser.py index 69e9fafa5..4398d2816 100644 --- a/src/braket/pulse/ast/approximation_parser.py +++ b/src/braket/pulse/ast/approximation_parser.py @@ -10,10 +10,12 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. + import re from collections import defaultdict +from collections.abc import KeysView from dataclasses import dataclass -from typing import Any, Dict, KeysView, List, Optional, Union +from typing import Any, Optional, Union import numpy as np from openpulse import ast @@ -43,7 +45,7 @@ class _FrameState: @dataclass class _ParseState: variables: dict - frame_data: Dict[str, _FrameState] + frame_data: dict[str, _FrameState] class _ApproximationParser(QASMVisitor[_ParseState]): @@ -52,12 +54,12 @@ class _ApproximationParser(QASMVisitor[_ParseState]): TIME_UNIT_TO_EXP = {"dt": 4, "ns": 3, "us": 2, "ms": 1, "s": 0} - def __init__(self, program: Program, frames: Dict[str, Frame]): + def __init__(self, program: Program, frames: dict[str, Frame]): self.amplitudes = defaultdict(TimeSeries) self.frequencies = defaultdict(TimeSeries) self.phases = defaultdict(TimeSeries) context = _ParseState(variables=dict(), frame_data=_init_frame_data(frames)) - self._qubit_frames_mapping: Dict[str, List[str]] = _init_qubit_frame_mapping(frames) + self._qubit_frames_mapping: dict[str, list[str]] = _init_qubit_frame_mapping(frames) self.visit(program.to_ast(include_externs=False), context) def visit( @@ -73,8 +75,8 @@ def visit( return super().visit(node, context) def _get_frame_parameters( - self, parameters: List[ast.Expression], context: _ParseState - ) -> Union[KeysView, List[str]]: + self, parameters: list[ast.Expression], context: _ParseState + ) -> Union[KeysView, list[str]]: frame_ids = set() for expression in parameters: identifier_name = self.visit(expression, context) @@ -466,7 +468,7 @@ def drag_gaussian(self, node: ast.FunctionCall, context: _ParseState) -> Wavefor return DragGaussianWaveform(*args) -def _init_frame_data(frames: Dict[str, Frame]) -> Dict[str, _FrameState]: +def _init_frame_data(frames: dict[str, Frame]) -> dict[str, _FrameState]: frame_states = dict() for frameId, frame in frames.items(): frame_states[frameId] = _FrameState( @@ -475,7 +477,7 @@ def _init_frame_data(frames: Dict[str, Frame]) -> Dict[str, _FrameState]: return frame_states -def _init_qubit_frame_mapping(frames: Dict[str, Frame]) -> Dict[str, List[str]]: +def _init_qubit_frame_mapping(frames: dict[str, Frame]) -> dict[str, list[str]]: mapping = {} for frameId in frames.keys(): if m := ( @@ -489,7 +491,7 @@ def _init_qubit_frame_mapping(frames: Dict[str, Frame]) -> Dict[str, List[str]]: return mapping -def _lcm_floats(*dts: List[float]) -> float: +def _lcm_floats(*dts: list[float]) -> float: """Return the least common multiple of time increments of a list of frames A time increment is the inverse of the corresponding sample rate which is considered an integer. @@ -497,7 +499,7 @@ def _lcm_floats(*dts: List[float]) -> float: Hence the LCM of dts is 1/gcd([sample rates]) Args: - *dts (List[float]): list of time resolutions + *dts (list[float]): list of time resolutions """ sample_rates = [round(1 / dt) for dt in dts] diff --git a/src/braket/pulse/ast/free_parameters.py b/src/braket/pulse/ast/free_parameters.py index 6cfc36d03..1581ddd88 100644 --- a/src/braket/pulse/ast/free_parameters.py +++ b/src/braket/pulse/ast/free_parameters.py @@ -10,7 +10,8 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Dict, Union + +from typing import Union from openpulse import ast from openqasm3.ast import DurationLiteral @@ -34,7 +35,7 @@ def expression(self) -> FreeParameterExpression: class _FreeParameterTransformer(QASMTransformer): """Walk the AST and evaluate FreeParameterExpressions.""" - def __init__(self, param_values: Dict[str, float]): + def __init__(self, param_values: dict[str, float]): self.param_values = param_values super().__init__() diff --git a/src/braket/pulse/ast/qasm_parser.py b/src/braket/pulse/ast/qasm_parser.py index 8e6f94ded..bd1b26e40 100644 --- a/src/braket/pulse/ast/qasm_parser.py +++ b/src/braket/pulse/ast/qasm_parser.py @@ -10,6 +10,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. + import io from openpulse import ast diff --git a/src/braket/pulse/ast/qasm_transformer.py b/src/braket/pulse/ast/qasm_transformer.py index ae4cccad4..f5e350883 100644 --- a/src/braket/pulse/ast/qasm_transformer.py +++ b/src/braket/pulse/ast/qasm_transformer.py @@ -10,6 +10,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. + from typing import Any, Optional from openpulse import ast diff --git a/src/braket/pulse/frame.py b/src/braket/pulse/frame.py index aab243a41..b84e4a440 100644 --- a/src/braket/pulse/frame.py +++ b/src/braket/pulse/frame.py @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. import math -from typing import Any, Dict, Optional +from typing import Any, Optional from oqpy import FrameVar as OQFrame from oqpy.base import OQPyExpression @@ -33,7 +33,7 @@ def __init__( frequency: float, phase: float = 0, is_predefined: bool = False, - properties: Optional[Dict[str, Any]] = None, + properties: Optional[dict[str, Any]] = None, ): """ Args: @@ -43,7 +43,7 @@ def __init__( phase (float): phase to which this frame should be initialized. Defaults to 0. is_predefined (bool): bool indicating whether this is a predefined frame on the device. Defaults to False. - properties (Optional[Dict[str, Any]]): Dict containing properties of this frame. + properties (Optional[dict[str, Any]]): Dict containing properties of this frame. Defaults to None. """ self._frame_id = frame_id diff --git a/src/braket/pulse/port.py b/src/braket/pulse/port.py index 373836875..2b1760415 100644 --- a/src/braket/pulse/port.py +++ b/src/braket/pulse/port.py @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import Any, Dict, Optional +from typing import Any, Optional from oqpy import PortVar from oqpy.base import OQPyExpression @@ -23,12 +23,12 @@ class Port: a device. See https://openqasm.com/language/openpulse.html#ports for more details. """ - def __init__(self, port_id: str, dt: float, properties: Optional[Dict[str, Any]] = None): + def __init__(self, port_id: str, dt: float, properties: Optional[dict[str, Any]] = None): """ Args: port_id (str): str identifying a unique port on the device. dt (float): The smallest time step that may be used on the control hardware. - properties (Optional[Dict[str, Any]]): Dict containing properties of + properties (Optional[dict[str, Any]]): Dict containing properties of this port. Defaults to None. """ self._port_id = port_id diff --git a/src/braket/pulse/pulse_sequence.py b/src/braket/pulse/pulse_sequence.py index 0a3dba154..e0808316b 100644 --- a/src/braket/pulse/pulse_sequence.py +++ b/src/braket/pulse/pulse_sequence.py @@ -16,7 +16,7 @@ import builtins from copy import deepcopy from inspect import signature -from typing import Any, Dict, List, Set, Union +from typing import Any, Union from openpulse import ast from oqpy import BitVar, PhysicalQubits, Program @@ -67,7 +67,7 @@ def to_time_trace(self) -> PulseSequenceTrace: ) @property - def parameters(self) -> Set[FreeParameter]: + def parameters(self) -> set[FreeParameter]: """Returns the set of `FreeParameter` s in the PulseSequence.""" return self._free_parameters.copy() @@ -169,14 +169,14 @@ def set_scale( def delay( self, - qubits_or_frames: Union[Frame, List[Frame], QubitSet], + qubits_or_frames: Union[Frame, list[Frame], QubitSet], duration: Union[float, FreeParameterExpression], ) -> PulseSequence: """ Adds an instruction to advance the frame clock by the specified `duration` value. Args: - qubits_or_frames (Union[Frame, List[Frame], QubitSet]): Qubits or frame(s) on which + qubits_or_frames (Union[Frame, list[Frame], QubitSet]): Qubits or frame(s) on which the delay needs to be introduced. duration (Union[float, FreeParameterExpression]): value (in seconds) defining the duration of the delay. @@ -199,13 +199,13 @@ def delay( self._program.delay(time=duration, qubits_or_frames=physical_qubits) return self - def barrier(self, qubits_or_frames: Union[List[Frame], QubitSet]) -> PulseSequence: + def barrier(self, qubits_or_frames: Union[list[Frame], QubitSet]) -> PulseSequence: """ Adds an instruction to align the frame clocks to the latest time across all the specified frames. Args: - qubits_or_frames (Union[List[Frame], QubitSet]): Qubits or frames which the delay + qubits_or_frames (Union[list[Frame], QubitSet]): Qubits or frames which the delay needs to be introduced. Returns: @@ -261,13 +261,13 @@ def capture_v0(self, frame: Frame) -> PulseSequence: self._frames[frame.id] = frame return self - def make_bound_pulse_sequence(self, param_values: Dict[str, float]) -> PulseSequence: + def make_bound_pulse_sequence(self, param_values: dict[str, float]) -> PulseSequence: """ Binds FreeParameters based upon their name and values passed in. If parameters share the same name, all the parameters of that name will be set to the mapped value. Args: - param_values (Dict[str, float]): A mapping of FreeParameter names + param_values (dict[str, float]): A mapping of FreeParameter names to a value to assign to them. Returns: @@ -334,7 +334,7 @@ def _format_parameter_ast( return parameter def _parse_arg_from_calibration_schema( - self, argument: Dict, waveforms: Dict[Waveform], frames: Dict[Frame] + self, argument: dict, waveforms: dict[Waveform], frames: dict[Frame] ) -> Any: nonprimitive_arg_type = { "frame": getattr(frames, "get"), @@ -348,15 +348,15 @@ def _parse_arg_from_calibration_schema( @classmethod def _parse_from_calibration_schema( - cls, calibration: Dict, waveforms: Dict[Waveform], frames: Dict[Frame] + cls, calibration: dict, waveforms: dict[Waveform], frames: dict[Frame] ) -> PulseSequence: """ Parsing a JSON input based on https://github.com/aws/amazon-braket-schemas-python/blob/main/src/braket/device_schema/pulse/native_gate_calibrations_v1.py#L26. Args: - calibration (Dict): The pulse instruction to parse - waveforms (Dict[Waveform]): The waveforms supplied for the pulse sequences. - frames (Dict[Frame]): A dictionary of frame objects to use. + calibration (dict): The pulse instruction to parse + waveforms (dict[Waveform]): The waveforms supplied for the pulse sequences. + frames (dict[Frame]): A dictionary of frame objects to use. Returns: PulseSequence: The parse sequence obtain from parsing a pulse instruction. @@ -427,7 +427,7 @@ def __eq__(self, other): def _validate_uniqueness( - mapping: Dict[str, Any], values: Union[Frame, Waveform, List[Frame], List[Waveform]] + mapping: dict[str, Any], values: Union[Frame, Waveform, list[Frame], list[Waveform]] ) -> None: if not isinstance(values, list): values = [values] diff --git a/src/braket/pulse/pulse_sequence_trace.py b/src/braket/pulse/pulse_sequence_trace.py index 9b4f55933..dba4762b2 100644 --- a/src/braket/pulse/pulse_sequence_trace.py +++ b/src/braket/pulse/pulse_sequence_trace.py @@ -14,7 +14,6 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Dict from braket.timings.time_series import TimeSeries @@ -34,6 +33,6 @@ class PulseSequenceTrace: the waveform phase. """ - amplitudes: Dict[str, TimeSeries] - frequencies: Dict[str, TimeSeries] - phases: Dict[str, TimeSeries] + amplitudes: dict[str, TimeSeries] + frequencies: dict[str, TimeSeries] + phases: dict[str, TimeSeries] diff --git a/src/braket/pulse/waveforms.py b/src/braket/pulse/waveforms.py index f298dd3e5..dbf89e146 100644 --- a/src/braket/pulse/waveforms.py +++ b/src/braket/pulse/waveforms.py @@ -16,7 +16,7 @@ import random import string from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Union +from typing import Optional, Union import numpy as np from oqpy import WaveformVar, bool_, complex128, declare_waveform_generator, duration, float64 @@ -57,12 +57,12 @@ def sample(self, dt: float) -> np.ndarray: @staticmethod @abstractmethod - def _from_calibration_schema(waveform_json: Dict) -> Waveform: + def _from_calibration_schema(waveform_json: dict) -> Waveform: """ Parses a JSON input and returns the BDK waveform. See https://github.com/aws/amazon-braket-schemas-python/blob/main/src/braket/device_schema/pulse/native_gate_calibrations_v1.py#L104 Args: - waveform_json (Dict): A JSON object with the needed parameters for making the Waveform. + waveform_json (dict): A JSON object with the needed parameters for making the Waveform. Returns: Waveform: A Waveform object parsed from the supplied JSON. @@ -73,10 +73,10 @@ class ArbitraryWaveform(Waveform): """An arbitrary waveform with amplitudes at each timestep explicitly specified using an array.""" - def __init__(self, amplitudes: List[complex], id: Optional[str] = None): + def __init__(self, amplitudes: list[complex], id: Optional[str] = None): """ Args: - amplitudes (List[complex]): Array of complex values specifying the + amplitudes (list[complex]): Array of complex values specifying the waveform amplitude at each timestep. The timestep is determined by the sampling rate of the frame to which waveform is applied to. id (Optional[str]): The identifier used for declaring this waveform. A random string of @@ -108,7 +108,7 @@ def sample(self, dt: float) -> np.ndarray: raise NotImplementedError @staticmethod - def _from_calibration_schema(waveform_json: Dict) -> ArbitraryWaveform: + def _from_calibration_schema(waveform_json: dict) -> ArbitraryWaveform: wave_id = waveform_json["waveformId"] complex_amplitudes = [complex(i[0], i[1]) for i in waveform_json["amplitudes"]] return ArbitraryWaveform(complex_amplitudes, wave_id) @@ -134,7 +134,7 @@ def __init__( self.id = id or _make_identifier_name() @property - def parameters(self) -> List[Union[FreeParameterExpression, FreeParameter, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, FreeParameter, float]]: """Returns the parameters associated with the object, either unbound free parameter expressions or bound values.""" return [self.length] @@ -186,7 +186,7 @@ def sample(self, dt: float) -> np.ndarray: return samples @staticmethod - def _from_calibration_schema(waveform_json: Dict) -> ConstantWaveform: + def _from_calibration_schema(waveform_json: dict) -> ConstantWaveform: wave_id = waveform_json["waveformId"] length = iq = None for val in waveform_json["arguments"]: @@ -239,7 +239,7 @@ def __init__( self.id = id or _make_identifier_name() @property - def parameters(self) -> List[Union[FreeParameterExpression, FreeParameter, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, FreeParameter, float]]: """Returns the parameters associated with the object, either unbound free parameter expressions or bound values.""" return [self.length, self.sigma, self.beta, self.amplitude] @@ -321,7 +321,7 @@ def sample(self, dt: float) -> np.ndarray: return samples @staticmethod - def _from_calibration_schema(waveform_json: Dict) -> DragGaussianWaveform: + def _from_calibration_schema(waveform_json: dict) -> DragGaussianWaveform: waveform_parameters = {"id": waveform_json["waveformId"]} for val in waveform_json["arguments"]: waveform_parameters[val["name"]] = ( @@ -363,7 +363,7 @@ def __init__( self.id = id or _make_identifier_name() @property - def parameters(self) -> List[Union[FreeParameterExpression, FreeParameter, float]]: + def parameters(self) -> list[Union[FreeParameterExpression, FreeParameter, float]]: """Returns the parameters associated with the object, either unbound free parameter expressions or bound values.""" return [self.length, self.sigma, self.amplitude] @@ -437,7 +437,7 @@ def sample(self, dt: float) -> np.ndarray: return samples @staticmethod - def _from_calibration_schema(waveform_json: Dict) -> GaussianWaveform: + def _from_calibration_schema(waveform_json: dict) -> GaussianWaveform: waveform_parameters = {"id": waveform_json["waveformId"]} for val in waveform_json["arguments"]: waveform_parameters[val["name"]] = ( @@ -464,7 +464,7 @@ def _map_to_oqpy_type( return parameter -def _parse_waveform_from_calibration_schema(waveform: Dict) -> Waveform: +def _parse_waveform_from_calibration_schema(waveform: dict) -> Waveform: waveform_names = { "arbitrary": ArbitraryWaveform._from_calibration_schema, "drag_gaussian": DragGaussianWaveform._from_calibration_schema, diff --git a/src/braket/quantum_information/pauli_string.py b/src/braket/quantum_information/pauli_string.py index 1f1454492..8dca06625 100644 --- a/src/braket/quantum_information/pauli_string.py +++ b/src/braket/quantum_information/pauli_string.py @@ -14,7 +14,7 @@ from __future__ import annotations import itertools -from typing import List, Optional, Tuple, Union +from typing import Optional, Union from braket.circuits.circuit import Circuit from braket.circuits.observables import TensorProduct, X, Y, Z @@ -86,7 +86,7 @@ def to_unsigned_observable(self) -> TensorProduct: [_PAULI_OBSERVABLES[self._nontrivial[qubit]] for qubit in sorted(self._nontrivial)] ) - def weight_n_substrings(self, weight: int) -> Tuple[PauliString, ...]: + def weight_n_substrings(self, weight: int) -> tuple[PauliString, ...]: r"""Returns every substring of this Pauli string with exactly `weight` nontrivial factors. The number of substrings is equal to :math:`\binom{n}{w}`, where :math`n` is the number of @@ -96,7 +96,7 @@ def weight_n_substrings(self, weight: int) -> Tuple[PauliString, ...]: weight (int): The number of non-identity factors in the substrings. Returns: - Tuple[PauliString, ...]: A tuple of weight-n Pauli substrings. + tuple[PauliString, ...]: A tuple of weight-n Pauli substrings. """ substrings = [] for indices in itertools.combinations(self._nontrivial, weight): @@ -111,7 +111,7 @@ def weight_n_substrings(self, weight: int) -> Tuple[PauliString, ...]: ) return tuple(substrings) - def eigenstate(self, signs: Optional[Union[str, List[int], Tuple[int, ...]]] = None) -> Circuit: + def eigenstate(self, signs: Optional[Union[str, list[int], tuple[int, ...]]] = None) -> Circuit: """Returns the eigenstate of this Pauli string with the given factor signs. The resulting eigenstate has each qubit in the +1 eigenstate of its corresponding signed @@ -120,7 +120,7 @@ def eigenstate(self, signs: Optional[Union[str, List[int], Tuple[int, ...]]] = N phase of the Pauli string is ignored). Args: - signs (Optional[Union[str, List[int], Tuple[int, ...]]]): The sign of each factor of the + signs (Optional[Union[str, list[int], tuple[int, ...]]]): The sign of each factor of the eigenstate, specified either as a string of "+" and "_", or as a list or tuple of +/-1. The length of signs must be equal to the length of the Pauli string. If not specified, it is assumed to be all +. Default: None. @@ -350,7 +350,7 @@ def __repr__(self): return f"{PauliString._phase_to_str(self._phase)}{''.join(factors)}" @staticmethod - def _split(pauli_word: str) -> Tuple[int, str]: + def _split(pauli_word: str) -> tuple[int, str]: index = 0 phase = 1 if pauli_word[index] in {"+", "-"}: @@ -367,7 +367,7 @@ def _split(pauli_word: str) -> Tuple[int, str]: def _phase_to_str(phase: int) -> str: return "+" if phase > 0 else "-" - def _generate_eigenstate_circuit(self, signs: Tuple[int, ...]) -> Circuit: + def _generate_eigenstate_circuit(self, signs: tuple[int, ...]) -> Circuit: circ = Circuit() for qubit in range(len(signs)): state = signs[qubit] * self[qubit] diff --git a/src/braket/registers/qubit_set.py b/src/braket/registers/qubit_set.py index ea012bc27..938d7fb50 100644 --- a/src/braket/registers/qubit_set.py +++ b/src/braket/registers/qubit_set.py @@ -13,7 +13,8 @@ from __future__ import annotations -from typing import Any, Dict, Iterable, Union +from collections.abc import Iterable +from typing import Any, Union from boltons.setutils import IndexedSet @@ -65,13 +66,13 @@ def __init__(self, qubits: QubitSetInput = None): _qubits = [Qubit.new(qubit) for qubit in _flatten(qubits)] if qubits is not None else None super().__init__(_qubits) - def map(self, mapping: Dict[QubitInput, QubitInput]) -> QubitSet: + def map(self, mapping: dict[QubitInput, QubitInput]) -> QubitSet: """ Creates a new `QubitSet` where this instance's qubits are mapped to the values in `mapping`. If this instance contains a qubit that is not in the `mapping` that qubit is not modified. Args: - mapping (Dict[QubitInput, QubitInput]): A dictionary of qubit mappings to + mapping (dict[QubitInput, QubitInput]): A dictionary of qubit mappings to apply. Key is the qubit in this instance to target, and the value is what the key will be changed to. diff --git a/src/braket/tasks/analog_hamiltonian_simulation_quantum_task_result.py b/src/braket/tasks/analog_hamiltonian_simulation_quantum_task_result.py index 74c0e848f..abc39753d 100644 --- a/src/braket/tasks/analog_hamiltonian_simulation_quantum_task_result.py +++ b/src/braket/tasks/analog_hamiltonian_simulation_quantum_task_result.py @@ -16,7 +16,6 @@ from collections import Counter from dataclasses import dataclass from enum import Enum -from typing import Dict, List import numpy as np @@ -53,7 +52,7 @@ def __eq__(self, other) -> bool: class AnalogHamiltonianSimulationQuantumTaskResult: task_metadata: TaskMetadata additional_metadata: AdditionalMetadata - measurements: List[ShotResult] = None + measurements: list[ShotResult] = None def __eq__(self, other) -> bool: if isinstance(other, AnalogHamiltonianSimulationQuantumTaskResult): @@ -90,7 +89,7 @@ def _from_object_internal( ) @classmethod - def _get_measurements(cls, result: AnalogHamiltonianSimulationTaskResult) -> List[ShotResult]: + def _get_measurements(cls, result: AnalogHamiltonianSimulationTaskResult) -> list[ShotResult]: measurements = [] for measurement in result.measurements: status = AnalogHamiltonianSimulationShotStatus(measurement.shotMetadata.shotStatus) @@ -105,7 +104,7 @@ def _get_measurements(cls, result: AnalogHamiltonianSimulationTaskResult) -> Lis measurements.append(ShotResult(status, pre_sequence, post_sequence)) return measurements - def get_counts(self) -> Dict[str, int]: + def get_counts(self) -> dict[str, int]: """Aggregate state counts from AHS shot results. Notes: @@ -115,7 +114,7 @@ def get_counts(self) -> Dict[str, int]: g: ground state atom Returns: - Dict[str, int]: number of times each state configuration is measured. + dict[str, int]: number of times each state configuration is measured. Returns None if none of shot measurements are successful. Only succesful shots contribute to the state count. """ diff --git a/src/braket/tasks/gate_model_quantum_task_result.py b/src/braket/tasks/gate_model_quantum_task_result.py index 9b21b0075..453dcacfb 100644 --- a/src/braket/tasks/gate_model_quantum_task_result.py +++ b/src/braket/tasks/gate_model_quantum_task_result.py @@ -15,8 +15,9 @@ import json from collections import Counter +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable, Dict, List, Optional, TypeVar, Union +from typing import Any, Optional, TypeVar, Union import numpy as np @@ -42,27 +43,27 @@ class GateModelQuantumTaskResult: Args: task_metadata (TaskMetadata): Quantum task metadata. additional_metadata (AdditionalMetadata): Additional metadata about the quantum task - result_types (List[Dict[str, Any]]): List of dictionaries where each dictionary + result_types (list[dict[str, Any]]): List of dictionaries where each dictionary has two keys: 'Type' (the result type in IR JSON form) and 'Value' (the result value for this result type). This can be an empty list if no result types are specified in the IR. This is calculated from `measurements` and the IR of the circuit program when `shots>0`. - values (List[Any]): The values for result types requested in the circuit. + values (list[Any]): The values for result types requested in the circuit. This can be an empty list if no result types are specified in the IR. This is calculated from `measurements` and the IR of the circuit program when `shots>0`. measurements (numpy.ndarray, optional): 2d array - row is shot and column is qubit. Default is None. Only available when shots > 0. The qubits in `measurements` are the ones in `GateModelQuantumTaskResult.measured_qubits`. - measured_qubits (List[int], optional): The indices of the measured qubits. Default + measured_qubits (list[int], optional): The indices of the measured qubits. Default is None. Only available when shots > 0. Indicates which qubits are in `measurements`. measurement_counts (Counter, optional): A `Counter` of measurements. Key is the measurements in a big endian binary string. Value is the number of times that measurement occurred. Default is None. Only available when shots > 0. Note that the keys in `Counter` are unordered. - measurement_probabilities (Dict[str, float], optional): + measurement_probabilities (dict[str, float], optional): A dictionary of probabilistic results. Key is the measurements in a big endian binary string. Value is the probability the measurement occurred. @@ -81,17 +82,17 @@ class GateModelQuantumTaskResult: task_metadata: TaskMetadata additional_metadata: AdditionalMetadata - result_types: List[ResultTypeValue] = None - values: List[Any] = None + result_types: list[ResultTypeValue] = None + values: list[Any] = None measurements: np.ndarray = None - measured_qubits: List[int] = None + measured_qubits: list[int] = None measurement_counts: Counter = None - measurement_probabilities: Dict[str, float] = None + measurement_probabilities: dict[str, float] = None measurements_copied_from_device: bool = None measurement_counts_copied_from_device: bool = None measurement_probabilities_copied_from_device: bool = None - _result_types_indices: Dict[str, int] = None + _result_types_indices: dict[str, int] = None def __post_init__(self): if self.result_types is not None: @@ -153,7 +154,7 @@ def measurement_counts_from_measurements(measurements: np.ndarray) -> Counter: @staticmethod def measurement_probabilities_from_measurement_counts( measurement_counts: Counter, - ) -> Dict[str, float]: + ) -> dict[str, float]: """ Creates measurement probabilities from measurement counts @@ -163,7 +164,7 @@ def measurement_probabilities_from_measurement_counts( occurred. Returns: - Dict[str, float]: A dictionary of probabilistic results. Key is the measurements + dict[str, float]: A dictionary of probabilistic results. Key is the measurements in a big endian binary string. Value is the probability the measurement occurred. """ measurement_probabilities = {} @@ -175,13 +176,13 @@ def measurement_probabilities_from_measurement_counts( @staticmethod def measurements_from_measurement_probabilities( - measurement_probabilities: Dict[str, float], shots: int + measurement_probabilities: dict[str, float], shots: int ) -> np.ndarray: """ Creates measurements from measurement probabilities. Args: - measurement_probabilities (Dict[str, float]): A dictionary of probabilistic results. + measurement_probabilities (dict[str, float]): A dictionary of probabilistic results. Key is the measurements in a big endian binary string. Value is the probability the measurement occurred. shots (int): number of iterations on device. @@ -352,8 +353,8 @@ def cast_result_types(gate_model_task_result: GateModelTaskResult) -> None: @staticmethod def _calculate_result_types( - ir_string: str, measurements: np.ndarray, measured_qubits: List[int] - ) -> List[ResultTypeValue]: + ir_string: str, measurements: np.ndarray, measured_qubits: list[int] + ) -> list[ResultTypeValue]: ir = json.loads(ir_string) result_types = [] if not ir.get("results"): @@ -402,7 +403,7 @@ def _calculate_result_types( @staticmethod def _selected_measurements( - measurements: np.ndarray, measured_qubits: List[int], targets: Optional[List[int]] + measurements: np.ndarray, measured_qubits: list[int], targets: Optional[list[int]] ) -> np.ndarray: if targets is not None and targets != measured_qubits: # Only some qubits targeted @@ -412,12 +413,12 @@ def _selected_measurements( @staticmethod def _calculate_for_targets( - calculate_function: Callable[[np.ndarray, List[int], Observable, List[int]], T], + calculate_function: Callable[[np.ndarray, list[int], Observable, list[int]], T], measurements: np.ndarray, - measured_qubits: List[int], + measured_qubits: list[int], observable: Observable, - targets: List[int], - ) -> Union[T, List[T]]: + targets: list[int], + ) -> Union[T, list[T]]: if targets: return calculate_function(measurements, measured_qubits, observable, targets) else: @@ -434,7 +435,7 @@ def _measurements_base_10(measurements: np.ndarray) -> np.ndarray: @staticmethod def _probability_from_measurements( - measurements: np.ndarray, measured_qubits: List[int], targets: Optional[List[int]] + measurements: np.ndarray, measured_qubits: list[int], targets: Optional[list[int]] ) -> np.ndarray: measurements = GateModelQuantumTaskResult._selected_measurements( measurements, measured_qubits, targets @@ -452,9 +453,9 @@ def _probability_from_measurements( @staticmethod def _variance_from_measurements( measurements: np.ndarray, - measured_qubits: List[int], + measured_qubits: list[int], observable: Observable, - targets: List[int], + targets: list[int], ) -> float: samples = GateModelQuantumTaskResult._samples_from_measurements( measurements, measured_qubits, observable, targets @@ -464,9 +465,9 @@ def _variance_from_measurements( @staticmethod def _expectation_from_measurements( measurements: np.ndarray, - measured_qubits: List[int], + measured_qubits: list[int], observable: Observable, - targets: List[int], + targets: list[int], ) -> float: samples = GateModelQuantumTaskResult._samples_from_measurements( measurements, measured_qubits, observable, targets @@ -476,9 +477,9 @@ def _expectation_from_measurements( @staticmethod def _samples_from_measurements( measurements: np.ndarray, - measured_qubits: List[int], + measured_qubits: list[int], observable: Observable, - targets: List[int], + targets: list[int], ) -> np.ndarray: measurements = GateModelQuantumTaskResult._selected_measurements( measurements, measured_qubits, targets diff --git a/src/braket/tasks/local_quantum_task_batch.py b/src/braket/tasks/local_quantum_task_batch.py index 6e36246e0..a47a47e6a 100644 --- a/src/braket/tasks/local_quantum_task_batch.py +++ b/src/braket/tasks/local_quantum_task_batch.py @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -from typing import List, Union +from typing import Union from braket.tasks import ( AnnealingQuantumTaskResult, @@ -29,7 +29,7 @@ class LocalQuantumTaskBatch(QuantumTaskBatch): def __init__( self, - results: List[ + results: list[ Union[ GateModelQuantumTaskResult, AnnealingQuantumTaskResult, @@ -41,7 +41,7 @@ def __init__( def results( self, - ) -> List[ + ) -> list[ Union[ GateModelQuantumTaskResult, AnnealingQuantumTaskResult, PhotonicModelQuantumTaskResult ] diff --git a/src/braket/tasks/quantum_task.py b/src/braket/tasks/quantum_task.py index 47ea8fff0..c306078ca 100644 --- a/src/braket/tasks/quantum_task.py +++ b/src/braket/tasks/quantum_task.py @@ -13,7 +13,7 @@ import asyncio from abc import ABC, abstractmethod -from typing import Any, Dict, Union +from typing import Any, Union from braket.tasks.annealing_quantum_task_result import AnnealingQuantumTaskResult from braket.tasks.gate_model_quantum_task_result import GateModelQuantumTaskResult @@ -62,7 +62,7 @@ def async_result(self) -> asyncio.Task: Task: Get the quantum task result asynchronously. """ - def metadata(self, use_cached_value: bool = False) -> Dict[str, Any]: + def metadata(self, use_cached_value: bool = False) -> dict[str, Any]: """ Get task metadata. @@ -71,6 +71,6 @@ def metadata(self, use_cached_value: bool = False) -> Dict[str, Any]: request. Default is False. Returns: - Dict[str, Any]: The metadata regarding the quantum task. If `use_cached_value` is True, + dict[str, Any]: The metadata regarding the quantum task. If `use_cached_value` is True, then the value retrieved from the most recent request is used. """ diff --git a/src/braket/tasks/quantum_task_batch.py b/src/braket/tasks/quantum_task_batch.py index 721afd7c1..ff0a0b82a 100644 --- a/src/braket/tasks/quantum_task_batch.py +++ b/src/braket/tasks/quantum_task_batch.py @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. from abc import ABC, abstractmethod -from typing import List, Union +from typing import Union from braket.tasks.annealing_quantum_task_result import AnnealingQuantumTaskResult from braket.tasks.gate_model_quantum_task_result import GateModelQuantumTaskResult @@ -25,13 +25,13 @@ class QuantumTaskBatch(ABC): @abstractmethod def results( self, - ) -> List[ + ) -> list[ Union[ GateModelQuantumTaskResult, AnnealingQuantumTaskResult, PhotonicModelQuantumTaskResult ] ]: """Get the quantum task results. Returns: - List[Union[GateModelQuantumTaskResult, AnnealingQuantumTaskResult, PhotonicModelQuantumTaskResult]]:: # noqa + list[Union[GateModelQuantumTaskResult, AnnealingQuantumTaskResult, PhotonicModelQuantumTaskResult]]:: # noqa Get the quantum task results. """ diff --git a/src/braket/timings/time_series.py b/src/braket/timings/time_series.py index 3baffe634..9d2b9cf00 100644 --- a/src/braket/timings/time_series.py +++ b/src/braket/timings/time_series.py @@ -14,11 +14,12 @@ from __future__ import annotations from collections import OrderedDict +from collections.abc import Iterator from dataclasses import dataclass from decimal import Decimal from enum import Enum from numbers import Number -from typing import Iterator, List, Union +from typing import Union @dataclass @@ -64,20 +65,20 @@ def put( self._sorted = False return self - def times(self) -> List[Number]: + def times(self) -> list[Number]: """Returns the times in the time series. Returns: - List[Number]: The times in the time series. + list[Number]: The times in the time series. """ self._ensure_sorted() return [item.time for item in self._series.values()] - def values(self) -> List[Number]: + def values(self) -> list[Number]: """Returns the values in the time series. Returns: - List[Number]: The values in the time series. + list[Number]: The values in the time series. """ self._ensure_sorted() return [item.value for item in self._series.values()] @@ -95,12 +96,12 @@ def _ensure_sorted(self) -> None: self._sorted = True @staticmethod - def from_lists(times: List[float], values: List[float]) -> TimeSeries: + def from_lists(times: list[float], values: list[float]) -> TimeSeries: """Create a time series from the list of time and value points Args: - times (List[float]): list of time points - values (List[float]): list of value points + times (list[float]): list of time points + values (list[float]): list of value points Returns: TimeSeries: time series constructed from lists @@ -117,18 +118,18 @@ def from_lists(times: List[float], values: List[float]) -> TimeSeries: return ts @staticmethod - def constant_like(times: Union[List[float], TimeSeries], constant: float = 0.0) -> TimeSeries: + def constant_like(times: Union[list[float], TimeSeries], constant: float = 0.0) -> TimeSeries: """Obtain a constant time series given another time series or the list of time points, and the constant values Args: - times (Union[List[float], TimeSeries]): list of time points or a time series + times (Union[list[float], TimeSeries]): list of time points or a time series constant (float): constant value Returns: TimeSeries: A constant time series """ - if not isinstance(times, List): + if not isinstance(times, list): times = times.times() ts = TimeSeries() @@ -280,12 +281,12 @@ def discretize(self, time_resolution: Decimal, value_resolution: Decimal) -> Tim return discretized_ts @staticmethod - def periodic_signal(times: List[float], values: List[float], num_repeat: int = 1) -> TimeSeries: + def periodic_signal(times: list[float], values: list[float], num_repeat: int = 1) -> TimeSeries: """Create a periodic time series by repeating the same block multiple times. Args: - times (List[float]): List of time points in a single block - values (List[float]): Values for the time series in a single block + times (list[float]): List of time points in a single block + values (list[float]): Values for the time series in a single block num_repeat (int): Number of block repeatitions Returns: diff --git a/src/braket/tracking/pricing.py b/src/braket/tracking/pricing.py index fb4b94699..2b049b251 100644 --- a/src/braket/tracking/pricing.py +++ b/src/braket/tracking/pricing.py @@ -17,7 +17,6 @@ import io import os from functools import lru_cache -from typing import Dict, List import urllib3 @@ -55,10 +54,10 @@ def get_prices(self) -> None: self._price_list = list(csv.DictReader(text_response)) @lru_cache() - def price_search(self, **kwargs) -> List[Dict[str, str]]: + def price_search(self, **kwargs) -> list[dict[str, str]]: """Searches the price list for a given set of parameters. Returns: - List[Dict[str, str]]: The price list. + list[dict[str, str]]: The price list. """ if not self._price_list: self.get_prices() diff --git a/src/braket/tracking/tracker.py b/src/braket/tracking/tracker.py index 9558d1d57..c30fc774e 100644 --- a/src/braket/tracking/tracker.py +++ b/src/braket/tracking/tracker.py @@ -16,7 +16,7 @@ from datetime import timedelta from decimal import Decimal from functools import singledispatchmethod -from typing import Any, Dict, List +from typing import Any from braket.tracking.pricing import price_search from braket.tracking.tracking_context import deregister_tracker, register_tracker @@ -66,12 +66,12 @@ def receive_event(self, event: _TaskCreationEvent) -> None: """ self._recieve_internal(event) - def tracked_resources(self) -> List[str]: + def tracked_resources(self) -> list[str]: """ Resources tracked by this tracker. Returns: - List[str]: The list of quantum task ids for quantum tasks tracked by this tracker. + list[str]: The list of quantum task ids for quantum tasks tracked by this tracker. """ return list(self._resources.keys()) @@ -117,12 +117,12 @@ def simulator_tasks_cost(self) -> Decimal: total_cost = total_cost + _get_simulator_task_cost(task_arn, details) return total_cost - def quantum_tasks_statistics(self) -> Dict[str, Dict[str, Any]]: + def quantum_tasks_statistics(self) -> dict[str, dict[str, Any]]: """ Get a summary of quantum tasks grouped by device. Returns: - Dict[str,Dict[str,Any]] : A dictionary where each key is a device arn, and maps to + dict[str,dict[str,Any]] : A dictionary where each key is a device arn, and maps to a dictionary sumarizing the quantum tasks run on the device. The summary includes the total shots sent to the device and the most recent status of the quantum tasks created on this device. For finished quantum tasks on simulator devices, the summary From cb7ed096a95764146c4c43e2615f7951ee055565 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 11 Oct 2023 16:16:26 +0000 Subject: [PATCH 25/28] prepare release v1.57.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 3e7fd4e97..3ea45409f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v1.57.2 (2023-10-11) + +### Bug Fixes and Other Changes + + * Use builtins for type hints + ## v1.57.1 (2023-10-05) ### Bug Fixes and Other Changes diff --git a/src/braket/_sdk/_version.py b/src/braket/_sdk/_version.py index a16206e2c..ec7889fbd 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.57.2.dev0" +__version__ = "1.57.2" From c4f07f5eebad41566cdf97cfed80b81313c08693 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 11 Oct 2023 16:16:26 +0000 Subject: [PATCH 26/28] update development version to v1.57.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 ec7889fbd..692565505 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.57.2" +__version__ = "1.57.3.dev0" From 0b8f1bde8099acda282e8889aad011a7fff936c9 Mon Sep 17 00:00:00 2001 From: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:31:23 -0400 Subject: [PATCH 27/28] Fix merge conflict --- src/braket/devices/local_simulator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/braket/devices/local_simulator.py b/src/braket/devices/local_simulator.py index f30a31c77..253732a06 100644 --- a/src/braket/devices/local_simulator.py +++ b/src/braket/devices/local_simulator.py @@ -108,7 +108,9 @@ def run_batch( self, task_specifications: Union[ Union[Circuit, Problem, Program, AnalogHamiltonianSimulation, SerializableProgram], - list[Union[Circuit, Problem, Program, AnalogHamiltonianSimulation, SerializableProgram]], + list[ + Union[Circuit, Problem, Program, AnalogHamiltonianSimulation, SerializableProgram] + ], ], shots: Optional[int] = 0, max_parallel: Optional[int] = None, @@ -307,7 +309,7 @@ def _( self, program: SerializableProgram, shots: Optional[int] = None, - inputs: Optional[Dict[str, float]] = None, + inputs: Optional[dict[str, float]] = None, *args, **kwargs, ): From c544b7749de713e2e73459ee135985a5fb1601dc Mon Sep 17 00:00:00 2001 From: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:35:19 -0400 Subject: [PATCH 28/28] Update mcm-sim branch commit --- setup.py | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 75ae42a75..a6541b816 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ # to get the version of the simulator that supports the mcm=True argument for Monte Carlo # simulation of mid-circuit measurement, which AutoQASM requires. # NOTE: This change should remain in the feature/autoqasm branch; do not merge to main. - "amazon-braket-default-simulator @ git+https://github.com/aws/amazon-braket-default-simulator-python.git@800a31a0860de5ec09532a3dbc127059d03cf9ac#egg=amazon-braket-default-simulator", # noqa E501 + "amazon-braket-default-simulator @ git+https://github.com/aws/amazon-braket-default-simulator-python.git@46aea776976ad7f958d847c06f29f3a7976f5cf5#egg=amazon-braket-default-simulator", # noqa E501 # Pin the latest commit of the qubit-array branch of ajberdy/oqpy.git to get the version of # oqpy which contains changes that AutoQASM relies on, including the QubitArray type. # NOTE: This change should remain in the feature/autoqasm branch; do not merge to main. diff --git a/tox.ini b/tox.ini index aa176cf64..367fd046c 100644 --- a/tox.ini +++ b/tox.ini @@ -142,4 +142,4 @@ commands = deps = # If you need to test on a certain branch, add @ after .git git+https://github.com/amazon-braket/amazon-braket-schemas-python.git - git+https://github.com/amazon-braket/amazon-braket-default-simulator-python.git@800a31a0860de5ec09532a3dbc127059d03cf9ac # mcm-sim branch + git+https://github.com/amazon-braket/amazon-braket-default-simulator-python.git@46aea776976ad7f958d847c06f29f3a7976f5cf5 # mcm-sim branch