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

Add result types to gate model result and make shots=0 default for simulators #70

Merged
merged 5 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 1 addition & 1 deletion examples/bell.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

# https://wikipedia.org/wiki/Bell_state
bell = Circuit().h(0).cnot(0, 1)
print(device.run(bell, s3_folder).result().measurement_counts)
print(device.run(bell, s3_folder, shots=100).result().measurement_counts)
7 changes: 4 additions & 3 deletions src/braket/aws/aws_qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, Union
from typing import Any, Dict, Union

import boto3
from braket.annealing.problem import Problem
Expand Down Expand Up @@ -64,7 +64,7 @@ def run(
self,
task_specification: Union[Circuit, Problem],
s3_destination_folder: AwsSession.S3DestinationFolder,
shots: Optional[int] = None,
shots: int = 1000,
*aws_quantum_task_args,
**aws_quantum_task_kwargs,
) -> AwsQuantumTask:
Expand All @@ -76,7 +76,8 @@ def run(
task_specification (Union[Circuit, Problem]): Specification of task
(circuit or annealing problem) to run on device.
s3_destination_folder: The S3 location to save the task's results
shots (Optional[int]): The number of times to run the circuit or annealing problem
shots (int, optional): The number of times to run the circuit or annealing problem.
Default is 1000.
*aws_quantum_task_args: Variable length positional arguments for
`braket.aws.aws_quantum_task.AwsQuantumTask.create()`.
**aws_quantum_task_kwargs: Variable length keyword arguments for
Expand Down
14 changes: 10 additions & 4 deletions src/braket/aws/aws_quantum_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, Union
from typing import Any, Dict, Union

from braket.annealing.problem import Problem
from braket.aws.aws_quantum_task import AwsQuantumTask
Expand All @@ -31,7 +31,7 @@ def __init__(self, arn: str, aws_session=None):
"""
Args:
arn (str): The ARN of the simulator, for example,
"arn:aws:aqx:::quantum-simulator:aqx:qs1".
"arn:aws:aqx:::quantum-simulator:aqx:qs1".
aws_session (AwsSession, optional) aws_session: An AWS session object. Default = None.
"""
super().__init__(name=None, status=None, status_reason=None)
Expand All @@ -44,7 +44,7 @@ def run(
self,
task_specification: Union[Circuit, Problem],
s3_destination_folder: AwsSession.S3DestinationFolder,
shots: Optional[int] = None,
shots: int = 0,
*aws_quantum_task_args,
**aws_quantum_task_kwargs,
) -> AwsQuantumTask:
Expand All @@ -55,7 +55,13 @@ def run(
task_specification (Union[Circuit, Problem]): Specification of task
(circuit or annealing problem) to run on device.
s3_destination_folder: The S3 location to save the task's results
shots (Optional[int]): The number of times to run the circuit or annealing problem
shots (int, optional): The number of times to run the circuit or annealing problem.
Default is 0.
For circuits, when `shots=0`, the simulator will support simulator-only
result types, compute the exact results based on the task specification,
and sampling is not supported.
`shots>0` means that the simulator will be treated like a QPU and
only support result types available for a QPU.
*aws_quantum_task_args: Variable length positional arguments for
`braket.aws.aws_quantum_task.AwsQuantumTask.create()`.
**aws_quantum_task_kwargs: Variable length keyword arguments for
Expand Down
16 changes: 12 additions & 4 deletions src/braket/aws/aws_quantum_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
import time
from functools import singledispatch
from logging import Logger, getLogger
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, Union

import boto3
from braket.annealing.problem import Problem
from braket.aws.aws_session import AwsSession
from braket.circuits.circuit import Circuit
from braket.circuits.circuit_helpers import validate_circuit_and_shots
from braket.tasks import AnnealingQuantumTaskResult, GateModelQuantumTaskResult, QuantumTask


Expand All @@ -46,7 +47,7 @@ def create(
device_arn: str,
task_specification: Union[Circuit, Problem],
s3_destination_folder: AwsSession.S3DestinationFolder,
shots: Optional[int] = None,
shots: int,
backend_parameters: Dict[str, Any] = None,
*args,
**kwargs,
Expand All @@ -68,20 +69,26 @@ def create(
to store task results in.

shots (int): The number of times to run the task on the device. If the device is a
simulator, this implies the state is sampled N times, where N = `shots`. Default
shots = 1_000.
simulator, this implies the state is sampled N times, where N = `shots`.
avawang1 marked this conversation as resolved.
Show resolved Hide resolved
`shots=0` is only available on simulators and means that the simulator
will compute the exact results based on the task specification.

backend_parameters (Dict[str, Any]): Additional parameters to send to the device.
For example, for D-Wave:
`{"dWaveParameters": {"postprocessingType": "OPTIMIZATION"}}`

Returns:
AwsQuantumTask: AwsQuantumTask tracking the task execution on the device.

Note:
The following arguments are typically defined via clients of Device.
- `task_specification`
- `s3_destination_folder`
- `shots`

See Also:
`braket.aws.aws_quantum_simulator.AwsQuantumSimulator.run()`
`braket.aws.aws_qpu.AwsQpu.run()`
"""
if len(s3_destination_folder) != 2:
raise ValueError(
Expand Down Expand Up @@ -351,6 +358,7 @@ def _(
*args,
**kwargs,
) -> AwsQuantumTask:
validate_circuit_and_shots(circuit, create_task_kwargs["shots"])
create_task_kwargs.update(
{
"ir": circuit.to_ir().json(),
Expand Down
37 changes: 37 additions & 0 deletions src/braket/circuits/circuit_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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, ResultType


def validate_circuit_and_shots(circuit: Circuit, shots: int) -> None:
"""
Validates if circuit and shots are correct before running on a device

Args:
circuit (Circuit): circuit to validate
shots (int): shots to validate

Raises:
ValueError: If no result types specified for circuit and shots=0.
See `braket.circuit.result_types. Or, if `StateVector` or `Amplitude`
are specified as result types when shots > 0.
"""
if not shots and not circuit.result_types:
raise ValueError(
"No result types specified for circuit and shots=0. See `braket.circuit.result_types`"
)
elif shots and circuit.result_types:
for rt in circuit.result_types:
if isinstance(rt, ResultType.StateVector) or isinstance(rt, ResultType.Amplitude):
raise ValueError("StateVector or Amplitude cannot be specified when shots>0")
avawang1 marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 22 additions & 4 deletions src/braket/circuits/result_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@


class StateVector(ResultType):
"""The full state vector as a requested result type."""
"""
The full state vector as a requested result type.
This is only available when `shots=0` for simulators.
"""

def __init__(self):
super().__init__(ascii_symbol=["StateVector"])
Expand Down Expand Up @@ -68,7 +71,10 @@ def __copy__(self) -> StateVector:


class Amplitude(ResultType):
"""The amplitude of specified quantum states as a requested result type."""
"""
The amplitude of specified quantum states as a requested result type.
This is only available when `shots=0` for simulators.
"""

def __init__(self, state: List[str]):
"""
Expand Down Expand Up @@ -129,10 +135,14 @@ def __copy__(self):


class Probability(ResultType):
"""Probability as the requested result type.
"""Probability in the computational basis as the requested result type.

It can be the probability of all states if no targets are specified or the marginal probability
of a restricted set of states if only a subset of all qubits are specified as target."""
of a restricted set of states if only a subset of all qubits are specified as target.

For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
only by simulators and represents the exact result.
"""

def __init__(self, target: QubitSetInput = None):
"""
Expand Down Expand Up @@ -260,6 +270,9 @@ class Expectation(ObservableResultType):
will be applied to all qubits in parallel. Otherwise, the number of specified targets
must be equivalent to the number of qubits the observable can be applied to.

For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
only by simulators and represents the exact result.

See :mod:`braket.circuits.observables` module for all of the supported observables.
"""

Expand Down Expand Up @@ -321,6 +334,8 @@ class Sample(ObservableResultType):
will be applied to all qubits in parallel. Otherwise, the number of specified targets
must be equivalent to the number of qubits the observable can be applied to.

This is only available for `shots>0`.

See :mod:`braket.circuits.observables` module for all of the supported observables.
"""

Expand Down Expand Up @@ -382,6 +397,9 @@ class Variance(ObservableResultType):
will be applied to all qubits in parallel. Otherwise, the number of specified targets
must be equivalent to the number of qubits the observable can be applied to.

For `shots>0`, this is calculated by measurements. For `shots=0`, this is supported
only by simulators and represents the exact result.

See :mod:`braket.circuits.observables` module for all of the supported observables.
"""

Expand Down
19 changes: 12 additions & 7 deletions src/braket/devices/local_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from braket.annealing.problem import Problem
from braket.circuits import Circuit
from braket.circuits.circuit_helpers import validate_circuit_and_shots
from braket.devices.braket_simulator import BraketSimulator
from braket.devices.device import Device
from braket.tasks import AnnealingQuantumTaskResult, GateModelQuantumTaskResult
Expand Down Expand Up @@ -51,12 +52,16 @@ def __init__(self, backend: Union[str, BraketSimulator] = "default"):
self._delegate = delegate

def run(
self, task_specification: Union[Circuit, Problem], *args, **kwargs,
self, task_specification: Union[Circuit, Problem], shots: int = 0, *args, **kwargs,
) -> LocalQuantumTask:
""" Runs the given task with the wrapped local simulator.

Args:
task_specification (Union[Circuit, Problem]):
shots (int, optional): The number of times to run the circuit or annealing problem.
Default is 0, which means that the simulator will compute the exact
results based on the task specification.
Sampling is not supported for shots=0.
*args: Positional args to pass to the IR simulator
**kwargs: Keyword arguments to pass to the IR simulator

Expand All @@ -73,7 +78,7 @@ def run(
>>> device = LocalSimulator("default")
>>> device.run(circuit, shots=1000)
"""
result = _run_internal(task_specification, self._delegate, *args, **kwargs)
result = _run_internal(task_specification, self._delegate, shots, *args, **kwargs)
return LocalQuantumTask(result)

@staticmethod
Expand Down Expand Up @@ -114,16 +119,16 @@ def _run_internal(


@_run_internal.register
def _(circuit: Circuit, simulator: BraketSimulator, shots: Optional[int] = None, *args, **kwargs):
def _(circuit: Circuit, simulator: BraketSimulator, shots, *args, **kwargs):
validate_circuit_and_shots(circuit, shots)
program = circuit.to_ir()
qubits = circuit.qubit_count
shots_count = shots if shots else LocalQuantumTask.DEFAULT_SHOTS
results_dict = simulator.run(program, qubits, shots=shots_count, *args, **kwargs)
results_dict = simulator.run(program, qubits, shots, *args, **kwargs)
return GateModelQuantumTaskResult.from_dict(results_dict)


@_run_internal.register
def _(problem: Problem, simulator: BraketSimulator, shots: Optional[int] = None, *args, **kwargs):
def _(problem: Problem, simulator: BraketSimulator, shots, *args, **kwargs):
ir = problem.to_ir()
results_dict = simulator.run(ir, *args, *kwargs)
results_dict = simulator.run(ir, shots, *args, *kwargs)
return AnnealingQuantumTaskResult.from_dict(results_dict)
Loading