Skip to content

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 on LocalSimulator, in the same process. Exact values (shots=0) are supported. Gradients use the general parameter-shift method.
  • A cloud simulator, when device_arn names SV1, DM1 or TN1. Amazon Web Services bills each task, and a single CostWarning is issued when the circuit is compiled.
  • A quantum processing unit (QPU), when device_arn contains /qpu/ (Rigetti, IonQ, IQM, AQT and others). shots > 0 is 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=True turns on parameter-shift gradients on the QPU, and compiling then issues a CostWarning that 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 handle.

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 False only for the shifted copies that compile_shifted builds. Default: True.

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 BraketJobHandle.task_id().

required
method str

The method that the task was submitted with, one of submit_methods.

'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 task_id is not the ARN of a Braket quantum task, if method is not in submit_methods, or if method is "run" and the observables need more than one task (see submit()). The Braket package is not called in any of these cases.

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 BraketCompiled handle for circuit.

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 float64 array of shape (2**n_qubits,), ordered with qubit 0 as the most significant bit, whose entries add up to 1.

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 weights or inputs is not 1-D. Nothing runs then.

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 weights.

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 0 as the first character of each key. The counts add up to the configured shots.

Raises:

Type Description
ValueError

If the configuration has shots=0 (a histogram needs shots), or if weights or inputs is not 1-D. Nothing runs then.

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 submit_methods. "run" (the default) measures the observables, like run(). "sample" returns the measurement histogram, like sample(). "probabilities" returns the distribution over every basis state, like probabilities(). "zero_state_probability" measures the all-zeros probability, like zero_state_probability().

'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 _ . : / = + - @. A key cannot start with aws:. Only cloud tasks are tagged, and the local simulator ignores the tags.

None

Returns:

Name Type Description
handle JobHandle

A handle for the submitted job.

Raises:

Type Description
ValueError

If method is not in submit_methods, if weights or inputs is not 1-D, if tags breaks the AWS tag rules, if method is "sample" with shots=0, if the device publishes no result type that method can read at the configured shots, or if the result type it reads is limited to fewer qubits than the circuit has. The limits are 15 qubits for Probability on a cloud device (List of result types), which "probabilities" and "zero_state_probability" read with shots > 0, and the exact limits listed under probabilities(). It is also raised if method is "run" and the observables need more than one task, because a handle tracks one task. This happens with shots > 0 when the terms do not all commute qubit-wise. Use run(), or split the observables into sets that commute. Nothing is submitted in any of these cases.

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 [0, 1].

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 Probability path or more than 8 qubits on the DM1 path. Nothing runs then.

BraketConfig dataclass

Device configuration for the Braket backend.

Parameters:

Name Type Description Default
device_arn str | None

None (the default) for the local simulator, or the Amazon Resource Name (ARN) of an Amazon Web Services (AWS) device. An example for a cloud simulator is arn:aws:braket:::device/quantum-simulator/amazon/sv1. Quantum processing units (QPUs) have ARNs of the form arn:aws:braket:<region>::device/qpu/<vendor>/<name>.

None
local_backend str

The LocalSimulator backend to use when device_arn is None. Common values are "braket_sv" (state vector) and "braket_dm" (density matrix).

'braket_sv'
shots int

0 means exact values (simulators only), and a positive integer turns on sampling with that number of shots. AWS QPUs require shots > 0.

0
s3_bucket str | None

The Amazon Simple Storage Service (S3) bucket for the results of cloud tasks. With None, the Braket package picks the default of the account.

None
s3_prefix str

The key prefix inside s3_bucket.

'braket-results'
poll_timeout_seconds int

How many seconds result() waits for a submitted task.

3600
enable_gradients bool

Whether to allow parameter-shift gradients on a QPU, for PyTorch .backward(), jacobian() and gradient-based optimisers. It is off by default, because one backward pass costs at least 2 * n_weights paid tasks. When it is set, compiling for a QPU issues a CostWarning that gives this count. Simulators compute gradients in either case.

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 BraketBackend for this configuration.

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 (coefficient, result index) pairs to add up.

required
poll_timeout_seconds int

How many seconds result() waits before giving up.

required
method str

The method of the compiled circuit that the task runs, one of BraketCompiled.submit_methods. It decides how result() decodes the values of the task.

'run'
n_qubits int | None

The number of qubits of the circuit. When it is given, result() checks that a "probabilities" or "zero_state_probability" task returned a distribution over 2**n_qubits states, and it writes the keys of a "sample" histogram as bitstrings of n_qubits characters.

None

Raises:

Type Description
ValueError

If method is not in BraketCompiled.submit_methods.

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 method="run", one float64 expectation value for each observable, of shape (n_obs,), exactly as BraketCompiled.run() returns. For method="sample", the measurement histogram as BraketCompiled.sample() returns it: the number of shots for each bitstring, with qubit 0 as the leftmost character. For method="probabilities", the float64 distribution of shape (2**n_qubits,) that BraketCompiled.probabilities() returns. For method="zero_state_probability", a zero-dimensional float64 array that holds the value that BraketCompiled.zero_state_probability() returns.

Raises:

Type Description
RuntimeError

If the task has no result, because it ended FAILED or CANCELLED, or because the wait timed out. The message names the task and its state.

ValueError

If the values of the task do not have the shape that the method and the circuit of this handle produce, for example because the handle was attached with the wrong method.

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.