braket_backend
eigenkit.core.backends.braket_backend
The Amazon Braket backend.
The amazon-braket-sdk package is an optional dependency
(pip install eigenkit[braket]). It is imported only when it is needed, so the
rest of the library works without it. If it is missing, compile() raises an
ImportError with a clear message.
BraketConfig selects one of three modes:
- The local simulator, the default, with
device_arn=None. The circuit runs onLocalSimulator, in the same process. Exact values (shots=0) are supported. Gradients use the general parameter-shift method. - A cloud simulator, when
device_arnnames SV1, DM1 or TN1. Amazon Web Services bills each task, and a singleCostWarningis issued when the circuit is compiled. - A quantum processing unit (QPU), when
device_arncontains/qpu/(Rigetti, IonQ, IQM, AQT and others).shots > 0is required. Gradients are turned off (gradient_method="none"), and calling.backward()raises an error whose message recommends training on a simulator and then using the weights on the device.enable_gradients=Trueturns on parameter-shift gradients on the QPU, and compiling then issues aCostWarningthat gives the number of tasks of one backward pass.
Both cloud modes can submit without waiting, through submit(), which returns a
BraketJobHandle. Its result() waits for the task, and its status() checks the
task without waiting. submit() can run run, sample, probabilities or
zero_state_probability (see BraketCompiled.submit_methods), and it can add
Amazon Web Services (AWS) tags to the task. attach() rebuilds a handle from the
Amazon Resource Name (ARN) of a task submitted earlier, for example after the
process that submitted it restarted.
sample reads the measurement counts of the task, so it needs shots > 0.
probabilities and zero_state_probability read the first result type that the
device publishes for the configured shots, in the order Probability,
StateVector, Amplitude, DensityMatrix (zero_state_probability skips
StateVector). With shots > 0, that is always Probability. At shots=0, the
managed simulators publish no Probability, so SV1 gives the exact answer through
Amplitude, and DM1 through DensityMatrix. The local simulator uses
Probability at any number of shots. Three of these paths are limited to the
number of qubits that Braket accepts, and wider circuits are refused before any
task is created. On a cloud device, Probability covers at most 15 qubits
(List of result types),
so both methods stop there with shots > 0. On the local simulator, only the
memory limits Probability. At shots=0, probabilities on SV1 lists every
basis state in the program, so it stops at 17 qubits (because of the program limit
of 5 megabytes), and DM1 returns a density matrix of at most 8 qubits. sample
has no limit.
compile() builds a parametric braket.circuits.Circuit, whose gates use
FreeParameter symbols, and adds one expectation result type for each distinct
Pauli term of all the observables. With shots=0, one circuit measures every
term. With shots > 0, one task can measure only terms that commute qubit-wise,
so the terms are split into such groups, with one circuit, and one task per call,
for each group. Each run or submit sets the parameter values through the
inputs={...} dictionary of the Braket package.
Example
from eigenkit import Circuit, Encoding, Variational, all_z
blocks = [Encoding(in_features=4), Variational(depth=2)]
# By registered name.
circuit = Circuit(blocks=blocks, observables=all_z(4), provider="braket")
# Or with the configuration, when a device setting matters.
from eigenkit.configs import BraketConfig
circuit = Circuit(
blocks=blocks,
observables=all_z(4),
provider=BraketConfig(
device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1"
),
)
Without a device_arn, this is the local simulator. With one, the circuit is
submitted to AWS. The ARN of a QPU turns gradients off (unless
enable_gradients=True) and requires shots.
BraketBackend
Provider for Amazon Braket, registered as "braket".
It runs on the local simulator by default, or on a cloud simulator or a
quantum processing unit (QPU) once device_arn is set in the configuration.
This class is internal: you select the backend with provider="braket" or by
passing a BraketConfig,
not by creating this class.
capabilities
property
What this backend supports.
It supports every gate except AmplitudeEncoding, CPHASE and
ControlledQubitUnitary, which this backend does not translate into Braket
circuits. It can sample with a finite number of shots. It has no native
batching, so run() loops over the samples itself. It has no limit on the
number of qubits. See
Capabilities.
gradient_method
property
How this backend computes gradients.
It is "parameter_shift" on a simulator. On a quantum processing unit
(QPU), it is "none", unless the configuration sets enable_gradients. A
parameter-shift backward pass costs at least two paid tasks per weight on
real hardware, so gradients are refused there unless you turn them on.
__init__(config=None, name='braket')
Build the backend from a device configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
BraketConfig
|
The device settings. |
None
|
name
|
str
|
The registry name, which error messages use. |
'braket'
|
compile(circuit, observables)
Prepare circuit for repeated evaluation of observables.
It is called once for each Circuit, and the handle is reused for every
call, so the expensive work happens here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables that the handle measures. |
required |
Returns:
| Type | Description |
|---|---|
BraketCompiled
|
A |
BraketCompiled
Bases: CompiledCircuit
A circuit prepared for Amazon Braket.
This class is internal: you get it from
BraketBackend.compile(),
and you never create it directly. run() evaluates the observables and waits
for the result. submit() returns a
BraketJobHandle for
a cloud or quantum processing unit (QPU) task, without waiting. attach()
rebuilds that handle from the Amazon Resource Name (ARN) of a task.
dry_run_info() reports how many circuits a call runs, without submitting
anything.
device
property
The local simulator or the AwsDevice, created when first read.
Authentication with Amazon Web Services (AWS) and network calls happen only when this property is first read.
submit_methods = _SUBMIT_METHODS
class-attribute
instance-attribute
The methods submit() and attach() accept: "run", "sample",
"probabilities" and "zero_state_probability". Any method added to this
adapter's async path must also be added here, or callers that check this
set will refuse it.
__init__(circuit, observables, config, *, warn=True)
Prepare the circuit for repeated evaluation.
BraketBackend.compile() builds it, and there is no reason to create one
directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables that this handle measures. |
required |
config
|
BraketConfig
|
The device settings. |
required |
warn
|
bool
|
Whether to issue the cost warnings for a cloud device. It
is |
True
|
attach(task_id, *, method='run')
Rebuild a job handle for a task submitted earlier from this circuit.
Use it to pick up a task after the process that submitted it has stopped,
for example after a crash. The handle decodes the results exactly as the
handle returned by submit() would have, provided that the task was
submitted from the same circuit, observables and method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
The Amazon Resource Name (ARN) of the Braket quantum
task, as returned by |
required |
method
|
str
|
The |
'run'
|
Returns:
| Name | Type | Description |
|---|---|---|
handle |
JobHandle
|
A handle for the existing task. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the configuration is not a cloud configuration, if
|
compile_shifted(circuit)
Compile a copy of this circuit with one gate angle shifted.
The chain-rule gradient compiles one copy for each shifted gate. The compilation of this handle already issued the cost warnings, so the copy issues none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The shifted circuit. |
required |
Returns:
| Type | Description |
|---|---|
'BraketCompiled'
|
A |
dry_run_info()
Report the size of the circuit without submitting anything.
Use it to check a cloud submission before you submit it. It reports how many circuits a call runs. It does not estimate a price.
probabilities(weights, inputs)
Return the measurement distribution over all 2**n_qubits basis states.
With shots > 0, it is estimated from the shot frequencies, through the
Probability result type. With shots=0, it is the exact distribution:
Probability on the local simulator, the squared amplitudes of every basis
state on SV1 (Amplitude), and the diagonal of the density matrix on DM1
(DensityMatrix). It is limited to _sampling.MAX_DENSE_QUBITS qubits,
and every cloud path has a lower limit. The limit is 15 qubits with
shots > 0, the widest Probability that the managed simulators and the
quantum processing units (QPUs) of Braket return
(List of result types).
It is 17 qubits on SV1 at shots=0, because the program lists all
2**n_qubits states and must stay under the Braket limit of 5 megabytes.
It is 8 qubits on DM1 at shots=0, the widest density matrix that DM1
returns. sample() has no limit on the number of qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
A 1-D parameter vector. |
required |
inputs
|
ndarray
|
A 1-D vector of input features. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
probabilities |
ndarray
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the device publishes none of the result types above
for the configured shots, if the circuit has more qubits than the
limit of the path taken, or if |
run(weights, inputs)
Evaluate the circuit and return one expectation value per observable.
It follows the CompiledCircuit contract: the result has shape (n_obs,)
when weights and inputs are both 1-D, and (B, n_obs) otherwise. With
shots > 0, each sample runs one task for each group of terms that commute
qubit-wise (see dry_run_info()["n_circuit_runs_per_call"]).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector: 1-D for one sample, or 2-D with a leading batch axis. |
required |
inputs
|
ndarray
|
The input features, with the same 1-D or 2-D shape
rule as |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
One expectation value for each observable (for each sample, in a batch). |
sample(weights, inputs)
Measure one sample in the computational basis and return the histogram.
It runs one task with the configured shots and no result type, and reads
the measurement counts of the task. A qubit that no gate acts on is
reported as 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
A 1-D parameter vector. |
required |
inputs
|
ndarray
|
A 1-D vector of input features. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
counts |
dict[str, int]
|
The number of shots for each measured
bitstring, with qubit |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the configuration has |
submit(weights, inputs, *, method='run', tags=None)
Submit the circuit without waiting, and return a BraketJobHandle.
The handle holds the Braket QuantumTask.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector of one sample, 1-D. |
required |
inputs
|
ndarray
|
The input features of one sample, 1-D. |
required |
method
|
str
|
What the task computes, one of |
'run'
|
tags
|
Mapping[str, str] | None
|
Amazon Web Services (AWS) tags to put
on the quantum task, to find it again later. There can be at most
50 tags. Keys have 1 to 128 characters and values at most 256
characters, made of letters, digits, spaces and |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
handle |
JobHandle
|
A handle for the submitted job. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
zero_state_probability(weights, inputs)
Return the probability of measuring all zeros at the end of the circuit.
It uses the Probability result type of Braket, which every Braket
simulator and quantum processing unit (QPU) supports with shots > 0, and
which the local simulator supports at any number of shots. The managed
simulators and the QPUs return it for at most 15 qubits
(List of result types),
so a wider circuit with shots > 0 on a cloud device is refused. In that
case, use sample(), which has no limit on the number of qubits, and
divide the all-zeros count by the shots. The local simulator has no limit.
With shots=0 on a cloud simulator, which publishes no Probability at
shots=0, the value is exact instead: the squared all-zeros amplitude on
SV1 (Amplitude), for any number of qubits, and the [0, 0] entry of the
density matrix on DM1 (DensityMatrix), which DM1 returns for at most 8
qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
A 1-D parameter vector. |
required |
inputs
|
ndarray
|
A 1-D vector of input features. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The probability, a float in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the device publishes none of those result types for
the configured shots, or if the circuit has more than 15 qubits on
the cloud |
BraketConfig
dataclass
Device configuration for the Braket backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_arn
|
str | None
|
|
None
|
local_backend
|
str
|
The |
'braket_sv'
|
shots
|
int
|
|
0
|
s3_bucket
|
str | None
|
The Amazon Simple Storage Service (S3) bucket for the results
of cloud tasks. With |
None
|
s3_prefix
|
str
|
The key prefix inside |
'braket-results'
|
poll_timeout_seconds
|
int
|
How many seconds |
3600
|
enable_gradients
|
bool
|
Whether to allow parameter-shift gradients on a QPU, for
PyTorch |
False
|
is_cloud
property
Whether device_arn is set.
If it is, the circuit runs on Amazon Web Services (AWS), on a cloud simulator or a quantum processing unit (QPU), instead of on the local simulator.
is_qpu
property
Whether device_arn names real hardware (it contains /qpu/).
make_backend()
Build the backend that this configuration describes.
The provider layer calls it when a configuration is passed as provider=.
There is no reason to call it directly.
Returns:
| Type | Description |
|---|---|
'BraketBackend'
|
The |
BraketJobHandle
A handle to a submitted Braket task.
It holds the Braket QuantumTask and the plan that turns the raw expectation
values of the task into one value for each Observable.
compiled.submit(weights, inputs) and compiled.attach(task_id) return one of
these handles. Call result() to wait until the task finishes, or status()
to check the task without waiting.
method
property
The method of the compiled circuit that this task runs.
It is "run", "sample", "probabilities" or "zero_state_probability".
__init__(task, aggregation_plan, poll_timeout_seconds, method='run', *, n_qubits=None)
Wrap a submitted Braket task in a job handle.
BraketCompiled.submit() or BraketCompiled.attach() builds it. It is not
meant to be created directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
QuantumTask
|
The Braket quantum task returned by the Braket package. |
required |
aggregation_plan
|
list[list[tuple[float, int]]]
|
How the raw result values map to the requested
observables. For each observable, it lists the
|
required |
poll_timeout_seconds
|
int
|
How many seconds |
required |
method
|
str
|
The method of the compiled circuit that the task runs,
one of |
'run'
|
n_qubits
|
int | None
|
The number of qubits of the circuit. When it is
given, |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
cancel()
Ask Braket to cancel the quantum task. It returns without waiting.
result()
Wait until the task finishes, then decode its values.
It waits for at most poll_timeout_seconds. The decoding follows the
method of this handle and the result type that the result of the task
actually holds, so a handle from attach() decodes a task exactly as the
handle returned by submit() would have.
Returns:
| Type | Description |
|---|---|
ndarray | dict[str, int]
|
For |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the task has no result, because it ended |
ValueError
|
If the values of the task do not have the shape that the
|
status()
Return the state of the task, without waiting.
The state is a string such as "CREATED", "QUEUED", "RUNNING",
"COMPLETED", "FAILED" or "CANCELLED".
task_id()
Return the identifier of the Braket quantum task, to look it up in the Amazon Web Services (AWS) console.