-
Notifications
You must be signed in to change notification settings - Fork 123
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
base: main
Are you sure you want to change the base?
Conversation
…OpenQASM3.0 sources
…f attribute strings; introduce PyTket-to-QASM translations
…nherit from EmulatorPass
…nstead of gate name strings in GateFidelity data class.
…s using QPU device properties
…l inside AwsDevice
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.
Still reviewing; great work though!
task_specification = emulator_pass(task_specification) | ||
return task_specification | ||
|
||
def run_validation_passes(self, task_specification: ProgramType) -> None: |
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.
Might be better to call this validate
def __init__(self, emulator_passes: Iterable[EmulatorPass] = None): | ||
self._emulator_passes = emulator_passes if emulator_passes is not None else [] | ||
|
||
def run_program_passes(self, task_specification: ProgramType) -> ProgramType: |
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.
May just run_passes
?
) | ||
|
||
|
||
def create_qubit_count_criterion(properties: DeviceCapabilities) -> QubitCountCriterion: |
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.
Just qubit_count_criterion
is fine; same goes for other create methods
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.
Updated!
return GateCriterion(supported_gates=supported_gates, native_gates=native_gates) | ||
|
||
|
||
@singledispatch |
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.
singledispatch
produces really ugly documentation when used on public functions; only use it on private methods, like so:
def connectivity_criterion(...):
return _connectivity_criterion(...)
@singledispatch
def _connectivity_criterion(...):
...
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.
Updated! Thank you for this callout, I wasn't aware of that interaction!
return GateConnectivityCriterion(gate_connectivity_graph) | ||
|
||
|
||
def get_qpu_gate_translation( |
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.
Are the names not already standardized across QPUs? This shouldn't be necessary.
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.
There are a few places where translation to a Braket gate name was necessary, i.e. gate names in Rigetti calibration data ("CPHASE" is used instead of the standard "cphaseshift") or in IonQs native gate set (translating "GPI"/"GPI2" to "GPi"/"GPi2"). I felt this was a little cleaner than doing translations in each place required.
src/braket/aws/aws_noise_models.py
Outdated
The following gate duration values are not available through Braket device | ||
calibration data and must be hardcoded. | ||
""" | ||
QPU_GATE_DURATIONS = { |
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.
Make this private; in fact, make things private whenever possible.
src/braket/aws/aws_noise_models.py
Outdated
self._validate_two_qubit_specs() | ||
|
||
|
||
def create_device_noise_model(properties: DeviceCapabilities, arn: str) -> NoiseModel: |
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.
device_noise_model
is sufficient
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.
Updated!
"""Runs the emulator pass on the provided program. | ||
Args: | ||
program (ProgramType): The program to run the emulator pass on. | ||
Returns: | ||
ProgramType: The program after the emulator pass has been applied. |
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.
nit
"""Runs the emulator pass on the provided program. | |
Args: | |
program (ProgramType): The program to run the emulator pass on. | |
Returns: | |
ProgramType: The program after the emulator pass has been applied. | |
"""Runs the emulator pass on the provided program. | |
Args: | |
program (ProgramType): The program to run the emulator pass on. | |
Returns: | |
ProgramType: The program after the emulator pass has been applied. |
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.
Updated!
src/braket/emulators/emulator_passes/criteria/connectivity_criterion.py
Outdated
Show resolved
Hide resolved
Args: | ||
circuit (Circuit): The Braket circuit whose gate operations to | ||
validate. | ||
""" |
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.
Should also add a Raises
section
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.
Added a Raises section to all methods where an exception is raised!
…e in aws_emulator_helpers; general documentation cleanup.
src/braket/emulators/emulator_passes/criteria/connectivity_criterion.py
Outdated
Show resolved
Hide resolved
or a valid DiGraph or dict representation of a connectivity graph is not provided. | ||
""" | ||
|
||
if not (connectivity_graph or fully_connected): |
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.
Maybe also raise an error if both are provided?
ProgramType = TypeVar("ProgramType") | ||
|
||
|
||
class EmulatorPass(ABC): |
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.
Should be EmulatorPass(ABC, Generic[ProgramType])
; also, how about EmulationPass
?
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.
Updated in b698648!
from abc import ABC, abstractmethod | ||
from typing import TypeVar | ||
|
||
ProgramType = TypeVar("ProgramType") |
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.
For generics, it's fine to use just T = TypeVar("T")
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.
I've opted to use "ProgramType
" just as it makes it clearer throughout code/documentation that an EmulatorPass expects some generic program as input - let me know if this approach makes sense!
src/braket/emulators/emulator_passes/criteria/emulator_criterion.py
Outdated
Show resolved
Hide resolved
src/braket/emulators/emulator_passes/criteria/gate_connectivity_criterion.py
Outdated
Show resolved
Hide resolved
src/braket/emulators/emulator_passes/criteria/connectivity_criterion.py
Outdated
Show resolved
Hide resolved
from braket.registers.qubit_set import QubitSet | ||
|
||
|
||
class ConnectivityCriterion(EmulatorCriterion): |
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.
ConnectivityCriterion(EmulatorCriterion[Circuit])
; also, how about naming all the Criterion
subclasses __Validators
?
src/braket/emulators/emulator_passes/criteria/gate_criterion.py
Outdated
Show resolved
Hide resolved
… program type in EmulatorPasses
…n a designated directory.
…Emulator._get_local_simulator_backend overwriting user provided backend name.
…dk-python into only_validation
…exception raised by an EmulatorPass in order to modify the message with the Emulator name.
…unsupported device capabilities
supported_gates: Optional[Iterator[str]] = None, | ||
native_gates: Optional[Iterator[str]] = None, |
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.
supported_gates: Optional[Iterator[str]] = None, | |
native_gates: Optional[Iterator[str]] = None, | |
supported_gates: Optional[Iterable[str]] = None, | |
native_gates: Optional[Iterable[str]] = None, |
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.
🤦♂️ Thanks for catching this, fixed!
src/braket/aws/aws_emulation.py
Outdated
return GateConnectivityValidator(gate_connectivity_graph) | ||
|
||
|
||
def _get_qpu_gate_translations( |
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.
I'll be very happy when this is no longer needed
self._supported_gates = set(BRAKET_GATES[gate.lower()] for gate in supported_gates) | ||
except KeyError as e: | ||
raise ValueError(f"Input {str(e)} in supported_gates is not a valid Braket gate name.") | ||
|
||
try: | ||
self._native_gates = set(BRAKET_GATES[gate.lower()] for gate in native_gates) |
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.
nit: frozenset
instead of set
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.
Changed to frozenset
!
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.
This is general enough that it perhaps belongs in its own module (maybe passes
or transpilation
)? The class itself might even be named just Pass
; at the very least, EmulationPass
is to specific for what it's actually capable of.
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.
I've moved it to a module called "passes" under the name "BasePass" (to try and avoid collisions with the pass
keyword even if the casing is different). Now, ValidationPass inherits directly from BasePass!
Looking good to me, let's see if @krneta has any feedback! |
Issue #, if available:
Description of changes:
This PR introduces the notion of device emulators and emulation passes to the BDK; device emulators provide some form of program compilation and validation and may apply a noise model to the program before simulating the program on the BDK LocalSimulator.
AwsDevice specific emulators are created based on the device properties and capabilities; noise models for gate-based QPUs are created based on calibration data retrieved from Braket service when creating an AwsDevice.
Testing done:
Unit testing covers 99% of emulator passes and noise model generation based on AwsDevice calibration data. (TODO: Complete unit tests for 100% coverage).
Merge Checklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.General
Tests
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.