Skip to content
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: Add noise operations and methods to add noise to circuits #149

Closed
wants to merge 48 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
84dbccb
feature: Add Noise classes
yitinche Aug 17, 2020
95888bc
feature: Add is_CPTP()
yitinche Aug 17, 2020
e9a4543
feature: Add add_noise() to Circuit
yitinche Aug 17, 2020
31f5084
feature: Allow printing Noise in circuit printout
yitinche Aug 17, 2020
01c4734
feature: Add NoiseMomentsKey()
yitinche Aug 17, 2020
821a286
feature: Add DensityMatrix as a result type
yitinche Aug 17, 2020
2357f73
Add tests for features related to noise simulator
yitinche Aug 17, 2020
3898019
fix: make changes for flake8 and remove unused import
yitinche Aug 18, 2020
e3659a7
fix: Update check for 0<= probability <=1
yitinche Aug 18, 2020
b0eb845
fix: check probability >=0 and <=1
yitinche Aug 19, 2020
99c0e61
feature: Move type checking of add_noise() input var to noise_helpers.py
yitinche Aug 19, 2020
a9cf99c
Add tests to increase unit-tests coverage
yitinche Aug 19, 2020
9ebc651
fix: linters
yitinche Aug 19, 2020
08008f1
Add tests to increase unit-test coverage
yitinche Aug 19, 2020
840561e
fix: Update condition for adding noise
yitinche Aug 19, 2020
0daa312
fix: Fix docstring and type hints
yitinche Aug 24, 2020
fb0dd0d
fix: Fix docstring and variable names
yitinche Aug 25, 2020
0caa432
fix: Update IR name in test: "Probability"->"SingleProbability"
yitinche Aug 25, 2020
6388a9b
feature: Add PhaseDamping noise
yitinche Aug 25, 2020
5f3f1f0
feature: Include add_noise() to Circuit.add()
yitinche Aug 25, 2020
2e20ca5
fix: Remove NoiseInsertStrategy and default to "inclusive" behavior
yitinche Aug 26, 2020
e21dc1b
fix: Merge the target checking into _add_noise_to_gates()
yitinche Aug 26, 2020
085995b
fix: Add check for len(target_qubits)==gate.qubit_count
yitinche Aug 26, 2020
1ea8f8b
fix: docstring and format
yitinche Aug 26, 2020
86d1fff
fix: Add checks and tests
yitinche Aug 26, 2020
eb88866
fix: Remove redundant methods
yitinche Aug 26, 2020
7b169f8
fix: Change name "ProbabilityNoise"->"ProbabilisticNoise"
yitinche Aug 26, 2020
53ccdae
fix: add tests and descriptions for noises.py
yitinche Aug 26, 2020
c71a9b3
fix: Add display name of noise channels in docstring
yitinche Aug 26, 2020
7e289f3
fix: linting from black's update
yitinche Aug 27, 2020
3937d9c
fix: Add integ-tests for noise simulator
yitinche Aug 27, 2020
fc5d38c
fix: change the input type of target_gates to Gate class
yitinche Sep 1, 2020
b2f27ca
fix: docstring and downcase CPTP->cptp
yitinche Sep 1, 2020
dba7759
fix: Add check for matrices of different sizes in Noise.Kraus
yitinche Sep 1, 2020
3d190fc
fix: add NoiseMomentKey to the type hint of Moments
yitinche Sep 1, 2020
a6b12ce
fix: Avoid looping through moments when add noise
yitinche Sep 1, 2020
e955961
fix: Fix the issue mentioned in #discussion_r480512575
yitinche Sep 1, 2020
2fb5b7d
fix: change math expression to LaTeX format
yitinche Sep 2, 2020
d4d8ccd
fix: docstring
yitinche Sep 2, 2020
b04c5d5
fix: Make helper methods public
yitinche Sep 2, 2020
86aa111
Merge https://github.com/aws/amazon-braket-sdk-python into noise
yitinche Sep 9, 2020
53a5f9b
change: Add example for local noise simulation
yitinche Sep 9, 2020
4d702f3
change: Update README.md
yitinche Sep 9, 2020
e77a631
update development version to v1.1.2.dev0
Sep 9, 2020
5916077
Merge branch 'main' into noise
amazon-braket-ci-bot Sep 9, 2020
5731cdc
use device data to create device level parameter data when creating a…
ajberdy Mar 29, 2021
f3af07a
use device data to create device level parameter data when creating a…
ajberdy Mar 29, 2021
69f49ad
Merge branch 'dwave-update' into noise
speller26 May 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ Amazon Braket provides access to two types of simulators: fully managed simulato

- Fully managed simulators offer high-performance circuit simulations. These simulators can handle circuits larger than circuits that run on quantum hardware. For example, the SV1 state vector simulator shown in the previous examples requires approximately 1 or 2 hours to complete a 34-qubit, dense, and square circuit (circuit depth = 34), depending on the type of gates used and other factors. For a list of available simulators and their features, consult the [Amazon Braket Developer Guide](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html).

- The Amazon Braket Python SDK includes an implementation of a quantum simulator that can run circuits on your local, classic hardware. The local simulator is well suited for rapid prototyping on small circuits up to 25 qubits, depending on the hardware specifications of your Braket notebook instance or your local environment. An example of how to execute the task locally is included in the repository `../examples/local_bell.py`.
- `LocalSimulator()` - The Amazon Braket Python SDK comes bundled with an implementation of a quantum simulator that you can run locally. The local simulator is well suited for rapid prototyping on small circuits up to 25 qubits or on small noisy circuits up to 13 qubits, depending on the hardware specifications of your Braket notebook instance or your local environment. An example of how to execute the task locally is included in the repo `../examples/local_bell.py` and `../examples/local_noise_simulation.py`.


### Debugging logs

Expand All @@ -117,14 +118,14 @@ device = AwsDevice("arn:aws:braket:::device/qpu/rigetti/Aspen-8")
s3_folder = ("amazon-braket-Your-Bucket-Name", "RIGETTI") # Use the S3 bucket you created during onboarding

bell = Circuit().h(0).cnot(0, 1)
task = device.run(bell, s3_folder)
task = device.run(bell, s3_folder)
print(task.result().measurement_counts)
```

When you execute your task, Amazon Braket polls for a result. By default, Braket polls for 5 days; however, it is possible to change this by modifying the `poll_timeout_seconds` parameter in `AwsDevice.run`, as in the example below. Keep in mind that if your polling timeout is too short, results may not be returned within the polling time, such as when a QPU is unavailable, and a local timeout error is returned. You can always restart the polling by using `task.result()`.

```python
task = device.run(bell, s3_folder, poll_timeout_seconds=86400) # 1 day
task = device.run(bell, s3_folder, poll_timeout_seconds=86400) # 1 day
print(task.result().measurement_counts)
```

Expand Down
30 changes: 30 additions & 0 deletions examples/local_noise_simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2019-2019 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.circuits import Circuit, Noise
from braket.devices import LocalSimulator

device = LocalSimulator(backend="noise_simulator")

circuit = Circuit().x(0).x(1).bit_flip(0, probability=0.1)
print("First example: ")
print(circuit)
print(device.run(circuit, shots=1000).result().measurement_counts)


circuit = Circuit().x(0).x(1)
noise = Noise.BitFlip(probability=0.1)
circuit.add_noise(noise)
print("Second example: ")
print(circuit)
print(device.run(circuit, shots=1000).result().measurement_counts)
2 changes: 2 additions & 0 deletions src/braket/_sdk/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
Version number (major.minor.patch[-label])
"""


__version__ = "1.5.16.dev0"

2 changes: 1 addition & 1 deletion src/braket/annealing/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ProblemType(str, Enum):


class Problem:
""" Represents an annealing problem."""
"""Represents an annealing problem."""

def __init__(
self,
Expand Down
50 changes: 47 additions & 3 deletions src/braket/aws/aws_quantum_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@
from braket.circuits.circuit import Circuit
from braket.circuits.circuit_helpers import validate_circuit_and_shots
from braket.device_schema import GateModelParameters
from braket.device_schema.dwave import DwaveDeviceParameters
from braket.device_schema.dwave import (
Dwave2000QDeviceParameters,
DwaveAdvantageDeviceParameters,
DwaveDeviceParameters,
)
from braket.device_schema.dwave.dwave_2000Q_device_level_parameters_v1 import (
Dwave2000QDeviceLevelParameters,
)
from braket.device_schema.dwave.dwave_advantage_device_level_parameters_v1 import (
DwaveAdvantageDeviceLevelParameters,
)
from braket.device_schema.ionq import IonqDeviceParameters
from braket.device_schema.rigetti import RigettiDeviceParameters
from braket.device_schema.simulators import GateModelSimulatorDeviceParameters
Expand Down Expand Up @@ -435,22 +445,56 @@ def _(
aws_session: AwsSession,
create_task_kwargs: Dict[str, Any],
device_arn: str,
device_parameters: Union[dict, DwaveDeviceParameters],
device_parameters: Union[
# TODO: add tests for initializing w device-specific parameters
dict,
DwaveDeviceParameters,
DwaveAdvantageDeviceParameters,
Dwave2000QDeviceParameters,
],
disable_qubit_rewiring,
*args,
**kwargs,
) -> AwsQuantumTask:
device_params = _create_annealing_device_params(device_parameters, device_arn)
create_task_kwargs.update(
{
"action": problem.to_ir().json(),
"deviceParameters": DwaveDeviceParameters.parse_obj(device_parameters).json(),
"deviceParameters": device_params.json(),
}
)

task_arn = aws_session.create_quantum_task(**create_task_kwargs)
return AwsQuantumTask(task_arn, aws_session, *args, **kwargs)


def _create_annealing_device_params(device_params, device_arn):
if type(device_params) is not dict:
device_params = device_params.dict()

device_level_parameters = device_params.get("deviceLevelParameters", None) or device_params.get(
"providerLevelParameters", None
)

if "braketSchemaHeader" in device_level_parameters:
del device_level_parameters["braketSchemaHeader"]

if "Advantage" in device_arn:
device_level_parameters = DwaveAdvantageDeviceLevelParameters.parse_obj(
device_level_parameters
)
return DwaveAdvantageDeviceParameters(deviceLevelParameters=device_level_parameters)
elif "2000Q" in device_arn:
device_level_parameters = Dwave2000QDeviceLevelParameters.parse_obj(device_level_parameters)
return Dwave2000QDeviceParameters(deviceLevelParameters=device_level_parameters)
else:
raise Exception(
f"Amazon Braket could not find a device with ARN: {device_arn}. "
"To continue, make sure that the value of the device_arn parameter "
"corresponds to a valid QPU."
)


def _create_common_params(
device_arn: str, s3_destination_folder: AwsSession.S3DestinationFolder, shots: int
) -> Dict[str, Any]:
Expand Down
2 changes: 2 additions & 0 deletions src/braket/circuits/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

# Execute initialization code in gates module
import braket.circuits.gates as gates # noqa: F401
import braket.circuits.noises as noises # noqa: F401
import braket.circuits.observables as observables # noqa: F401
import braket.circuits.result_types as result_types # noqa: F401
from braket.circuits.angled_gate import AngledGate # noqa: F401
Expand All @@ -25,6 +26,7 @@
from braket.circuits.gate import Gate # noqa: F401
from braket.circuits.instruction import Instruction # noqa: F401
from braket.circuits.moments import Moments, MomentsKey # noqa: F401
from braket.circuits.noise import Noise # noqa: F401
from braket.circuits.observable import Observable, StandardObservable # noqa: F401
from braket.circuits.operator import Operator # noqa: F401
from braket.circuits.quantum_operator import QuantumOperator # noqa: F401
Expand Down
5 changes: 3 additions & 2 deletions src/braket/circuits/ascii_circuit_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from braket.circuits.circuit_diagram import CircuitDiagram
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

Expand Down Expand Up @@ -102,8 +103,8 @@ def _ascii_group_items(
"""
groupings = []
for item in items:
# Can only print Gate operators for instructions at the moment
if isinstance(item, Instruction) and not isinstance(item.operator, Gate):
# Can only print Gate and Noise operators for instructions at the moment
if isinstance(item, Instruction) and not isinstance(item.operator, (Gate, Noise)):
yitinche marked this conversation as resolved.
Show resolved Hide resolved
continue

if isinstance(item, ResultType) and not item.target:
Expand Down
134 changes: 129 additions & 5 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@

from __future__ import annotations

from typing import Callable, Dict, Iterable, List, Tuple, TypeVar, Union
from typing import Callable, Dict, Iterable, List, Optional, Type, TypeVar, Union


from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.moments import Moments
from braket.circuits.noise import Noise
from braket.circuits.noise_helpers import add_noise_to_gates, add_noise_to_moments
from braket.circuits.observable import Observable
from braket.circuits.observables import TensorProduct
from braket.circuits.qubit import QubitInput
Expand Down Expand Up @@ -485,13 +489,129 @@ def add_circuit(

return self

def add_noise(
self,
noise: Noise,
target_gates: Optional[Union[Type[Gate], Iterable[Type[Gate]]]] = None,
target_qubits: Optional[QubitSetInput] = None,
target_times: Optional[Union[int, Iterable[int]]] = None,
) -> Circuit:
"""Add `noise` to the circuit according to `target_gates`, `target_qubits` and
`target_times`.

For any parameter that is None, that specification is ignored (e.g. if `target_gates`
and `target_qubits` are None then the noise is added to all qubits at `target_times`).
If `target_gates`, `target_qubits`, and `target_times` are all None, then `noise` is
added to every qubit at every moment.

When `noise.qubit_count` == 1, ie. `noise` is single-qubit, `noise` is added to all
qubits in `target_gates` (or to all qubits in `target_qubits` if `target_gates` is None).

When `noise.qubit_count` > 1 and `target_gates` is not None, `noise.qubit_count`
must be the same as `qubit_count` of gates specified by `target_gates`.

When `noise.qubit_count` > 1 and `target_gates` is None, `noise.qubit_count` must
be the same as the length of `target_qubits`.

Args:
noise (Noise): Noise to be added to the circuit. When `noise.qubit_count` > 1,
`noise.qubit_count` must be the same as `qubit_count` of gates specified by
`target_gates`.
target_gates (Union[Type[Gate], Iterable[Type[Gate]], optional]): Gate class or
List of Gate classes which `noise` is added to. Default=None.
target_qubits (Union[QubitSetInput, optional]): Index or indices of qubit(s).
Default=None.
target_times (Union[int, Iterable[int], optional]): Index of indices of time which
`noise` is added to. Default=None.

Returns:
Circuit: self

Raises:
yitinche marked this conversation as resolved.
Show resolved Hide resolved
TypeError: If `noise` is not Noise type, `target_gates` is not a Gate class,
Iterable[Gate] or None, `target_times` is not int or Iterable[int] or None.
ValueError:
If `noise.qubit_count` is not equal to `len(gate.target)` for gates in
`target_gates`. If `noise.qubit_count` is not equal to `len(target_qubits)`
when `target_gates` is None.

Examples:
>>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1)
>>> print(circ)
T : |0|1|2|
q0 : -X-Z-C-
|
q1 : -Y-X-X-
T : |0|1|2|
>>> noise = Noise.Depolarizing(probability=0.1)
>>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1)
>>> print(circ.add_noise(noise, target_gates = 'X'))
T : | 0 | 1 |2|
q0 : -X-ND(0.1)-Z---------C-
|
q1 : -Y---------X-ND(0.1)-X-
T : | 0 | 1 |2|
>>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1)
>>> print(circ.add_noise(noise, target_qubits = 1))
T : | 0 | 1 | 2 |
q0 : -X---------Z---------C---------
|
q1 : -Y-ND(0.1)-X-ND(0.1)-X-ND(0.1)-
T : | 0 | 1 | 2 |
>>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1)
>>> print(circ.add_noise(noise, target_times=[0,2]))
T : | 0 |1| 2 |
q0 : -X-ND(0.1)-Z-C-ND(0.1)-
|
q1 : -Y-ND(0.1)-X-X-ND(0.1)-
T : | 0 |1| 2 |
>>> circ = Circuit().x(0).y(1).z(0).x(1).cnot(0,1)
>>> print(circ.add_noise(noise,
... target_gates = ['X','Y'],
... target_qubits = [0,1],
... target_times=1)
... )
T : |0| 1 |2|
q0 : -X-Z---------C-
|
q1 : -Y-X-ND(0.1)-X-
T : |0| 1 |2|

"""

if isinstance(target_gates, type) and issubclass(target_gates, Gate):
target_gates = [target_gates]
if isinstance(target_times, int):
target_times = [target_times]
if target_qubits is not None:
target_qubits = QubitSet(target_qubits)

if not isinstance(noise, Noise):
raise TypeError("noise must be an instance of the Noise class")
if target_gates is not None:
if not (
isinstance(target_gates, Iterable)
and all(isinstance(g, type) and issubclass(g, Gate) for g in target_gates)
):
raise TypeError("all elements in target_gates must be a Gate class")
if target_times is not None:
if not (
isinstance(target_times, Iterable) and all(isinstance(t, int) for t in target_times)
):
raise TypeError("all elements in target_times must be int")

if target_gates is None:
return add_noise_to_moments(self, noise, target_qubits, target_times)
else:
return add_noise_to_gates(self, noise, target_gates, target_qubits, target_times)

def add(self, addable: AddableTypes, *args, **kwargs) -> Circuit:
yitinche marked this conversation as resolved.
Show resolved Hide resolved
"""
Generic add method for adding item(s) to self. Any arguments that
yitinche marked this conversation as resolved.
Show resolved Hide resolved
`add_circuit()` and / or `add_instruction()` and / or `add_result_type`
supports are supported by this method. If adding a subroutine,
check with that subroutines documentation to determine what input it
allows.
and / or `add_noise` supports are supported by this method. If adding a
subroutine, check with that subroutines documentation to determine what
input it allows.

Args:
addable (AddableTypes): The item(s) to add to self. Default = `None`.
Expand All @@ -511,6 +631,8 @@ def add(self, addable: AddableTypes, *args, **kwargs) -> Circuit:

`add_result_type()`

`add_noise()`

Examples:
>>> circ = Circuit().add([Instruction(Gate.H(), 4), Instruction(Gate.CNot(), [4, 5])])
>>> circ = Circuit().add([ResultType.StateVector()])
Expand All @@ -532,7 +654,9 @@ def _flatten(addable):
yield addable

for item in _flatten(addable):
if isinstance(item, Instruction):
if isinstance(item, Noise):
self.add_noise(item, *args, **kwargs)
elif isinstance(item, Instruction):
self.add_instruction(item, *args, **kwargs)
elif isinstance(item, ResultType):
self.add_result_type(item, *args, **kwargs)
Expand Down
Loading