-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: Introduce Validating Emulators with Noise Models to the SDK #1017
Open
Altanali
wants to merge
111
commits into
amazon-braket:main
Choose a base branch
from
Altanali:only_validation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
111 commits
Select commit
Hold shift + click to select a range
ea85675
feat: Introduce emulator criterion
ltnln 28ec84e
feat: Introduce NativeGateCriterion + Unit Tests
ltnln f41c52b
feat: Introduce SupportedGateCriterion + Unit Tests
ltnln fee7888
feat: Introduce Connectivity Criterion + Unit Tests
ltnln 6f216ab
feat: Add __init__.py to emulators/criteria module
ltnln 4e909e5
feat: Add OpenQASM3.0-to-Pytket translations
ltnln e3e4593
feat: Introduce PytketProgramContext to generate PytketCircuits from …
ltnln eda10b7
feat: Begin emulator pass implementation
ltnln 096aae7
feat: Change gate translations to use pytket OpType objects instead o…
ltnln 0483fb9
Merge branch 'add_pytket_context' into add_tket_mapping_pass
ltnln a801ad4
feat: Introduce Pytket-to-Openqasm Translators
ltnln 36eff02
fix: Covert from radians to Tket half-turns in PytketContext
ltnln a328916
feat: add MEASUREMENT_REGISTER_NAME to translation constants
ltnln 94b4226
feat: Introduce EmulatorPass abstraction and have EmulatorCriterion i…
ltnln 1d37b0a
fix: Update __init__.pys for emulator_pass and criteria directories
ltnln 52e0465
fix: Correct import paths in emulator_passes source and tests
ltnln 467c27c
feat: Introduce Gate Connectivity Criterion
ltnln 5dca87d
fix: Correct qubit order in error print statement
ltnln 4c92658
feat: Add GateConnectivityCriterion tests
ltnln 88c9530
feat: Basic AWS Noise Model
ltnln 1ff8cfa
fix: Discard IonQ Single Qubit Gate Fidelities and use Gate classes i…
ltnln 3355759
Merge branch 'emulation_criteria_basic' into end_to_end_prototype
ltnln edef55d
fix: Include EmulatorCriterion in __init__.py for emulator_passes module
ltnln 14d09e8
feat: Introduce Emulator Interface and AwsEmulator
ltnln c229adc
fix: Fix gate device noise model file name typo
ltnln 362ef45
Merge branch 'add_gate_connectivity' into end_to_end_prototype
ltnln 74fecd0
fix: Remove stray print statement
ltnln 28e79bc
feat: Introduce emulators and instantiate emulators within AWS device…
ltnln dc4a9cf
feat: Introduce Qubit Count Criteria
ltnln 56e730e
feat: Move NoiseModel generation to a set of helper functions and cal…
ltnln cf0af85
fix: Clean Up Branch
ltnln d802eaf
Merge branch 'add_pytket_context' into end_to_end_prototype
ltnln b14cf12
feat: Complete tket_to_qasm3 translations
ltnln d648b87
fix: Return Qasm3 string instead of program context from tket_to_qasm…
ltnln 0ecd0fa
fix: Add tket_to_qasm3 to pytket_translator module __init__.py
ltnln bc19df5
feat: Introduce LexiRoutingPass
ltnln 7c1ac20
feat: Split out mapping into a run() dispatch with a pytket circuit a…
ltnln ad202d7
fix: Add LexiRoutingPass to emulator_passes module __init__.py
ltnln 50c53cd
fix: Implement __eq__ in qubit_count_criterion and fix error print st…
ltnln 10a1dee
feat: AwsDevices add mapping/routing pass to their emulator based on …
ltnln 49809ba
feat: Add validation and compile methods to AwsDevice
ltnln a3f8459
fix: Update connectivity criterion to validate connectivity of ALL 2-…
ltnln 4c8039b
fix: Remove stray print statement
ltnln d4bd18c
fix: Add classical indices to add_measure instruction in Tket Context
ltnln d9c9ede
fix: Add classical targets option to add_measure instruction in Brake…
ltnln 5fc4437
Merge branch 'main' into end_to_end_prototype
ltnln e2c4b55
fix: Update add_instruction to update circuit _measurement_targets at…
ltnln 695c415
Merge branch 'introduce_classical_measure_targets' into end_to_end_pr…
ltnln f1de87a
feat: Merge SupportedGateCriterion and NativeGateCriterion classes in…
ltnln 8c1b3ec
fix: Allow GateConnectivityCriterion to be instantiated with a graph …
ltnln d38432b
feat: Allow ConnectivityGraphs to be instantiated with a graph marked…
ltnln a4906d9
feat: Add QubitCountCriterion tests and check if qubit_count is negat…
ltnln 39bbfaa
change: remove SpportedGateCriterion and NativeGateCriterion after re…
ltnln dcc061e
feat: Add tests for tket_to_qasm3 translation, remove extraneous comm…
ltnln 4b207ee
change: Remove references to NativeGateCriterion and SupportedGateCri…
ltnln 80f94d0
Merge branch 'amazon-braket:main' into end_to_end_prototype
Altanali 7268799
fix: Add set apply_noise_model flag to false in Emulator.run to preve…
ltnln b236055
Merge branch 'main' into introduce_classical_measure_targets
Altanali a55edb2
change: Target default simulator branch with classical register indic…
ltnln e39941d
fix: Run linter and fix formatting/complexity
ltnln 9416ffb
Merge branch 'introduce_classical_measure_targets' into end_to_end_pr…
ltnln ac49677
feat: Run Linter
ltnln 7b3ebf3
fix: Revert default simulator versions and dependent round_trip tests
ltnln b5828ad
Update src/braket/circuits/braket_program_context.py
speller26 6b0ad92
Update src/braket/circuits/braket_program_context.py
speller26 ece64a4
Update src/braket/circuits/braket_program_context.py
speller26 7caf75e
Merge branch 'main' into introduce_classical_measure_targets
speller26 19b78ab
Merge branch 'main' of https://github.com/amazon-braket/amazon-braket…
ltnln 51296d9
Merge branch 'introduce_classical_measure_targets'
ltnln 532451c
Merge branch 'introduce_classical_measure_targets'
ltnln e254410
Merge branch 'main' into main
speller26 ae3a0e3
fix: Add round_trip test for classical target tracking during measure…
ltnln 8756960
change: Run Linter
ltnln 82d24e3
Merge branch 'main' into prevent_verbatim_circuit_mapping
ltnln c6e60f1
fix: Remove measurements from Pytket circuit before performing mapping
ltnln 5bec8d4
feat: Add decompose bridge pass after Tket Routing
ltnln 40bf179
feat: Add emulator names to error messages and use AwsDevice names wh…
ltnln 3e1b47c
fix: Fix mistake with applying RB calibration data if sRB data is una…
ltnln 3f5094e
fix: Fix add_pass to correctly check if iterable supplied as argument
ltnln 66703d7
fix: Return nothing in AwsDevice validate if program is valid.
ltnln ac070e2
change: Remove tket passes and files, fix emulator_interface.py filen…
ltnln c37bac4
fix: Update EmulatorInterface import in Emulator module
ltnln 545c8ce
fix: Remove references to LexiRouting pass throughout repo.
ltnln afbb96c
feat!: Run linter, add comments/documentation, add unit tests for emu…
ltnln 934b0ae
test!: Add unit tests and run linters
ltnln b787222
test: Add AwsNoise model tests
ltnln 8080de7
Merge branch 'main' into only_validation
speller26 6cd4ffc
test: Reach 100% coverage, run linters
ltnln 1e16ba1
change: Change task_specification type in validate documentation
ltnln 87d342b
Merge branch 'only_validation' of github.com:Altanali/amazon-braket-s…
ltnln fdfa8c0
fix: Use explicit ProgramType as Python3.9 does not support type para…
ltnln 20c59a2
test: Add test for IonQ create_noise_model dispatch for native gates …
ltnln 073fb4e
fix: Place local_simulator._simulator_device setup in a fixture rathe…
ltnln 3c9fd67
fix: Compare arn strings against explicit enum string values
ltnln 0601c63
change: Update documentation, add check to GateConnectivityCriterion …
ltnln c5b9fc3
change: Clean up GateConnectivityCriterion instantiation logic.
ltnln 34b94ae
change: Add Raises documentation and make dispatched functions privat…
ltnln 70881c6
Merge branch 'amazon-braket:main' into only_validation
Altanali b698648
change: Use 'validator' in place of 'criterion', fix usage of generic…
ltnln e1cb307
change: Reorganize modules to place all gate-device emulator passes i…
ltnln 3b500a3
fix: Correct __init__.pys for emulator_passes module and remove circu…
ltnln 7d7279e
change: Rename emulator_passes directory to emulation_passes
ltnln 8c63da6
change: Remove outdated connectivity_criterion.py file
ltnln df11f66
Merge branch 'main' into only_validation
Altanali a79e07d
fix: Use deepcopy before applying emulation passes to a program, fix …
ltnln 96e917a
Merge branch 'only_validation' of github.com:Altanali/amazon-braket-s…
ltnln f30379c
fix: Fix Emulator raising a new Exception instance from the original …
ltnln 9bddfbc
change: Change error message when trying to create a NoiseModel from …
ltnln 4626332
feat: Replace 'EmulationPass' with more general BasePass class
ltnln e3ca8a5
change: Remove mentions of 'emulation' from the BasePass module.
ltnln baccbb1
change: Fix style in BasePass.py
ltnln File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
from collections.abc import Iterable | ||
from functools import singledispatch | ||
from typing import Union | ||
|
||
from networkx import DiGraph | ||
|
||
from braket.device_schema import DeviceActionType, DeviceCapabilities | ||
from braket.device_schema.ionq import IonqDeviceCapabilities | ||
from braket.device_schema.iqm import IqmDeviceCapabilities | ||
from braket.device_schema.rigetti import RigettiDeviceCapabilities | ||
from braket.emulation.emulation_passes.gate_device_passes import ( | ||
ConnectivityValidator, | ||
GateConnectivityValidator, | ||
GateValidator, | ||
QubitCountValidator, | ||
) | ||
|
||
|
||
def qubit_count_validator(properties: DeviceCapabilities) -> QubitCountValidator: | ||
""" | ||
Create a QubitCountValidator pass which checks that the number of qubits used in a program does | ||
not exceed the number of qubits allowed by a QPU, as defined in the device properties. | ||
|
||
Args: | ||
properties (DeviceCapabilities): QPU Device Capabilities object with a | ||
QHP-specific schema. | ||
|
||
Returns: | ||
QubitCountValidator: An emulator pass that checks that the number of qubits used in a | ||
program does not exceed that of the max qubit count on the device. | ||
""" | ||
qubit_count = properties.paradigm.qubitCount | ||
return QubitCountValidator(qubit_count) | ||
|
||
|
||
def gate_validator(properties: DeviceCapabilities) -> GateValidator: | ||
""" | ||
Create a GateValidator pass which defines what supported and native gates are allowed in a | ||
program based on the provided device properties. | ||
|
||
Args: | ||
properties (DeviceCapabilities): QPU Device Capabilities object with a | ||
QHP-specific schema. | ||
|
||
Returns: | ||
GateValidator: An emulator pass that checks that a circuit only uses supported gates and | ||
verbatim circuits only use native gates. | ||
""" | ||
|
||
supported_gates = properties.action[DeviceActionType.OPENQASM].supportedOperations | ||
native_gates = properties.paradigm.nativeGateSet | ||
|
||
return GateValidator(supported_gates=supported_gates, native_gates=native_gates) | ||
|
||
|
||
def connectivity_validator( | ||
properties: DeviceCapabilities, connectivity_graph: DiGraph | ||
) -> ConnectivityValidator: | ||
""" | ||
Creates a ConnectivityValidator pass which validates that two-qubit gates are applied to | ||
connected qubits based on this device's connectivity graph. | ||
|
||
Args: | ||
properties (DeviceCapabilities): QPU Device Capabilities object with a | ||
QHP-specific schema. | ||
|
||
connectivity_graph (DiGraph): Connectivity graph for this device. | ||
|
||
Returns: | ||
ConnectivityValidator: An emulator pass that checks that a circuit only applies two-qubit | ||
gates to connected qubits on the device. | ||
""" | ||
|
||
return _connectivity_validator(properties, connectivity_graph) | ||
|
||
|
||
@singledispatch | ||
def _connectivity_validator( | ||
properties: DeviceCapabilities, connectivity_graph: DiGraph | ||
) -> ConnectivityValidator: | ||
|
||
connectivity_validator = ConnectivityValidator(connectivity_graph) | ||
return connectivity_validator | ||
|
||
|
||
@_connectivity_validator.register(IqmDeviceCapabilities) | ||
def _(properties: IqmDeviceCapabilities, connectivity_graph: DiGraph) -> ConnectivityValidator: | ||
""" | ||
IQM qubit connectivity is undirected but the directed graph that represents qubit connectivity | ||
does not include back-edges. Thus, we must explicitly introduce back edges before creating | ||
the ConnectivityValidator for an IQM device. | ||
""" | ||
connectivity_graph = connectivity_graph.copy() | ||
for edge in connectivity_graph.edges: | ||
connectivity_graph.add_edge(edge[1], edge[0]) | ||
return ConnectivityValidator(connectivity_graph) | ||
|
||
|
||
def gate_connectivity_validator( | ||
properties: DeviceCapabilities, connectivity_graph: DiGraph | ||
) -> GateConnectivityValidator: | ||
return _gate_connectivity_validator(properties, connectivity_graph) | ||
|
||
|
||
@singledispatch | ||
def _gate_connectivity_validator( | ||
properties: DeviceCapabilities, connectivity_graph: DiGraph | ||
) -> GateConnectivityValidator: | ||
raise NotImplementedError | ||
|
||
|
||
@_gate_connectivity_validator.register(IqmDeviceCapabilities) | ||
@_gate_connectivity_validator.register(RigettiDeviceCapabilities) | ||
def _( | ||
properties: RigettiDeviceCapabilities, connectivity_graph: DiGraph | ||
) -> GateConnectivityValidator: | ||
""" | ||
Both IQM and Rigetti have undirected connectivity graphs; Rigetti device capabilities | ||
provide back edges, but the calibration data only provides edges in one direction. | ||
Additionally, IQM does not provide back edges in its connectivity_graph (nor is this | ||
resolved manually by AwsDevice at the moment). | ||
""" | ||
gate_connectivity_graph = connectivity_graph.copy() | ||
edge_properties = properties.standardized.twoQubitProperties | ||
for u, v in gate_connectivity_graph.edges: | ||
edge_key = "-".join([str(qubit) for qubit in (u, v)]) | ||
edge_property = edge_properties.get(edge_key) | ||
|
||
# Check that the QHP provided calibration data for this edge. | ||
if not edge_property: | ||
gate_connectivity_graph[u][v]["supported_gates"] = set() | ||
continue | ||
edge_supported_gates = _get_qpu_gate_translations( | ||
properties, [property.gateName for property in edge_property.twoQubitGateFidelity] | ||
) | ||
gate_connectivity_graph[u][v]["supported_gates"] = set(edge_supported_gates) | ||
|
||
# Add the reversed edge to ensure gates can be applied | ||
# in both directions for a given qubit pair. | ||
for u, v in gate_connectivity_graph.edges: | ||
if (v, u) not in gate_connectivity_graph.edges or gate_connectivity_graph[v][u].get( | ||
"supported_gates" | ||
) in [None, set()]: | ||
gate_connectivity_graph.add_edge( | ||
v, u, supported_gates=set(gate_connectivity_graph[u][v]["supported_gates"]) | ||
) | ||
|
||
return GateConnectivityValidator(gate_connectivity_graph) | ||
|
||
|
||
@_gate_connectivity_validator.register(IonqDeviceCapabilities) | ||
def _(properties: IonqDeviceCapabilities, connectivity_graph: DiGraph) -> GateConnectivityValidator: | ||
""" | ||
Qubits in IonQ's trapped ion devices are all fully connected with identical | ||
gate-pair capabilities. IonQ does not expliclty provide a set of edges for | ||
gate connectivity between qubit pairs in their trapped ion QPUs. | ||
We extrapolate gate connectivity across all possible qubit edge pairs. | ||
""" | ||
gate_connectivity_graph = connectivity_graph.copy() | ||
native_gates = _get_qpu_gate_translations(properties, properties.paradigm.nativeGateSet) | ||
|
||
for edge in gate_connectivity_graph.edges: | ||
gate_connectivity_graph[edge[0]][edge[1]]["supported_gates"] = set(native_gates) | ||
|
||
return GateConnectivityValidator(gate_connectivity_graph) | ||
|
||
|
||
def _get_qpu_gate_translations( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll be very happy when this is no longer needed |
||
properties: DeviceCapabilities, gate_name: Union[str, Iterable[str]] | ||
) -> Union[str, list[str]]: | ||
"""Returns the translated gate name(s) for a given QPU device capabilities schema type | ||
and gate name(s). | ||
|
||
Args: | ||
properties (DeviceCapabilities): Device capabilities object based on a | ||
device-specific schema. | ||
gate_name (Union[str, Iterable[str]]): The name(s) of the gate(s). If gate_name is a list | ||
of string gate names, this function attempts to retrieve translations of all the gate | ||
names. | ||
|
||
Returns: | ||
Union[str, list[str]]: The translated gate name(s) | ||
""" | ||
if isinstance(gate_name, str): | ||
return _get_qpu_gate_translation(properties, gate_name) | ||
else: | ||
return [_get_qpu_gate_translation(properties, name) for name in gate_name] | ||
|
||
|
||
@singledispatch | ||
def _get_qpu_gate_translation(properties: DeviceCapabilities, gate_name: str) -> str: | ||
"""Returns the translated gate name for a given QPU ARN and gate name. | ||
|
||
Args: | ||
properties (DeviceCapabilities): QPU Device Capabilities object with a | ||
QHP-specific schema. | ||
gate_name (str): The name of the gate | ||
|
||
Returns: | ||
str: The translated gate name | ||
""" | ||
return gate_name | ||
|
||
|
||
@_get_qpu_gate_translation.register(RigettiDeviceCapabilities) | ||
def _(properties: RigettiDeviceCapabilities, gate_name: str) -> str: | ||
translations = {"CPHASE": "CPhaseShift"} | ||
return translations.get(gate_name, gate_name) | ||
|
||
|
||
@_get_qpu_gate_translation.register(IonqDeviceCapabilities) | ||
def _(properties: IonqDeviceCapabilities, gate_name: str) -> str: | ||
translations = {"GPI": "GPi", "GPI2": "GPi2"} | ||
return translations.get(gate_name, gate_name) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the behavior when shots is None, is it just shots=0 and we return the final density matrix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The arguments and results of Emulator.emulate() follows that of
LocalSimulator.run
; if no result types are provided (and if shots=0),emulate()
raises an exception (ValueError: No result types specified for circuit and shots=0. See braket.circuits.result_types
) just as the LocalSimulator would.