pub struct Simulate<'c, SeedState> { /* private fields */ }Expand description
Builder for query-aware simulation requests.
Implementations§
Source§impl<'c> Simulate<'c, Seeded>
impl<'c> Simulate<'c, Seeded>
Sourcepub fn braket_results(self, specs: &[ResultSpec]) -> Result<Vec<ResultValue>>
pub fn braket_results(self, specs: &[ResultSpec]) -> Result<Vec<ResultValue>>
Evaluate the result requests a Braket program declared.
Every expectation and variance request is served by a single
traversal: their observables are lowered to Pauli sums, the distinct
strings across all of them are evaluated together through
Simulate::expectation_values, and each requested value is then a
weighted sum over that one evaluation. state_vector and amplitude
share one export, and a probability or density_matrix request beside
them is read off that same export. Without one it runs on its own,
keeping the routing its own width earns: a subset marginal of a wide
Clifford circuit stays on the tableau rather than forcing a dense
export.
sample is declined here: it reports per-shot eigenvalues, which
Simulate::braket_results_sampled answers. So is a circuit carrying a
measurement, reset or conditional, which has no one exact output state
to read: Braket rejects the same programs at zero shots.
§Errors
Returns BackendUnsupported for a sample request,
IncompatibleBackend for a circuit that is not unitary, and whatever
the underlying terminal returns for a route that cannot serve a request.
Sourcepub fn braket_results_sampled(
self,
specs: &[ResultSpec],
shots: usize,
) -> Result<Vec<ResultValue>>
pub fn braket_results_sampled( self, specs: &[ResultSpec], shots: usize, ) -> Result<Vec<ResultValue>>
Evaluate the result requests a Braket program declared, from a shot record rather than from the exact state.
Each observable is diagonalized and the rotations carrying them onto the
computational basis are appended to the circuit once, so a single
sampling pass answers every sample, expectation and variance
request together. Two observables reading one qubit in different bases
cannot share a record and are rejected rather than answered from
whichever basis was applied first. A probability request reads the
computational basis and so takes its own unrotated pass whenever any
rotation was applied.
§Errors
Returns BackendUnsupported for state_vector, density_matrix and
amplitude, which report the state itself and which Braket admits only
at zero shots, and InvalidParameter for zero shots or for observables
that cannot share one measurement.
Source§impl<'c, SeedState> Simulate<'c, SeedState>
impl<'c, SeedState> Simulate<'c, SeedState>
Sourcepub fn backend(self, kind: BackendKind) -> Self
pub fn backend(self, kind: BackendKind) -> Self
Select an explicit backend kind instead of BackendKind::Auto routing.
Sourcepub fn require_exact(self) -> Self
pub fn require_exact(self) -> Self
Reject a route that could return an approximate answer, rather than taking it and saying so in the result.
BackendKind::Auto sends a circuit past the statevector cap to an MPS
at a bounded bond dimension, which is the only route those circuits have;
the result reports Exactness::Approximate either way. Call this when
an approximate answer is worse than no answer, and the run returns
IncompatibleBackend naming the engine it would have used.
Routes that can be decided from the circuit are rejected before any state is allocated; sparse Pauli dynamics only learns that it truncated while propagating, so that one is caught on the finished result instead.
Sourcepub fn noise(self, model: &'c NoiseModel) -> Self
pub fn noise(self, model: &'c NoiseModel) -> Self
Attach a noise model.
Simulate::shots and Simulate::sample_counts accept one on any
backend with a per-shot pure state, averaging trajectories.
Simulate::run, Simulate::marginals,
Simulate::expectation_values and
Simulate::reduced_density_matrix answer from the exact mixture
instead, which only BackendKind::DensityMatrix and its device
sibling hold, so they require one of those, as does
Simulate::expectation_gradient_shift and, only to decline on it,
Simulate::entanglement_entropy: a mixture has no Schmidt
decomposition. Simulate::expectation_gradient rejects a noise model
on every backend.
Sourcepub fn initial_state(self, amplitudes: &'c [Complex64]) -> Self
pub fn initial_state(self, amplitudes: &'c [Complex64]) -> Self
Start from amplitudes instead of |0…0⟩.
Indexed with qubit 0 in the least significant bit, length 2^n for the
circuit’s n qubits, and normalized. A vector failing any of those is
rejected with InvalidParameter before the run.
A start state also constrains the route, because shape-based dispatch
reads the circuit alone and its shortcuts hold only from |0…0⟩:
BackendKind::Auto resolves to the statevector, and every backend
other than the statevector (dense, device, or distributed) and
BackendKind::DensityMatrix reports IncompatibleBackend.
Simulate::expectation_gradient declines a start
state, as do Simulate::shots and Simulate::sample_counts with a
noise model attached, since trajectory replay has no start-state path.
Sourcepub fn gpu(self, context: Arc<GpuContext>) -> Self
Available on crate feature gpu only.
pub fn gpu(self, context: Arc<GpuContext>) -> Self
gpu only.Shortcut for Simulate::backend with BackendKind::StatevectorGpu.
Sourcepub fn gpu_auto(self, context: Arc<GpuContext>) -> Self
Available on crate feature gpu only.
pub fn gpu_auto(self, context: Arc<GpuContext>) -> Self
gpu only.Automatic backend selection with GPU acceleration opted in via context.
Routes like BackendKind::Auto, but a selected statevector or
stabilizer block that clears the qubit crossover with VRAM to spare runs
on the device. Unsupported cases fall back to the identical CPU path.
Sourcepub fn distributed(self, context: Arc<DistributedContext>) -> Self
Available on crate feature distributed only.
pub fn distributed(self, context: Arc<DistributedContext>) -> Self
distributed only.Distribute the exact state vector across the ranks of context.
With a single rank this behaves like Simulate::backend with
BackendKind::Statevector.
Source§impl<'c> Simulate<'c, Seeded>
impl<'c> Simulate<'c, Seeded>
Sourcepub fn run(self) -> Result<RunOutcome>
pub fn run(self) -> Result<RunOutcome>
Execute the circuit once.
With a noise model attached the probabilities are the exact noisy
distribution rather than one trajectory, so the run needs the
density-matrix backend; the classical bits are one draw, matching
shots(1). Readout error reaches the draw and not the state, so a
model carrying it is rejected rather than answered with two fields from
different distributions; shots and sample_counts apply it.
Sourcepub fn shots(self, num_shots: usize) -> Result<ShotsResult>
pub fn shots(self, num_shots: usize) -> Result<ShotsResult>
Execute num_shots times, collecting per-shot classical bits. Accepts
an attached noise model.
Sourcepub fn sample_counts(self, num_shots: usize) -> Result<CountsResult>
pub fn sample_counts(self, num_shots: usize) -> Result<CountsResult>
Sample a frequency histogram over num_shots executions. Accepts an
attached noise model.
Counts may be sampled directly from the output distribution, so seeded
counts can differ from Simulate::shots plus ShotsResult::counts
while drawing from the identical distribution.
Sourcepub fn marginals(self) -> Result<MarginalsResult>
pub fn marginals(self) -> Result<MarginalsResult>
Per-qubit marginal probabilities as (P(0), P(1)) pairs. Rejects
backends without probability output, and with a noise model attached
answers exactly from the mixture, which needs the density-matrix
backend, and rejects a model carrying readout error, since
sample_counts is the terminal that applies it.
Sourcepub fn expectation_values(
self,
observables: &[Vec<PauliTerm>],
) -> Result<Vec<f64>>
pub fn expectation_values( self, observables: &[Vec<PauliTerm>], ) -> Result<Vec<f64>>
Compute ⟨ψ|P|ψ⟩ for each joint Pauli observable on the circuit’s
output state, honoring the selected backend.
Each observable is a product of single-qubit Paulis (identity factors
omitted). The circuit must be unitary. Clifford circuits propagate each
observable exactly. Non-Clifford circuits use the state vector while they
fit it; above that cap the selected backend evaluates the observable on
its own representation, and a backend without one reports
BackendUnsupported naming itself.
With a noise model attached the value is the exact Tr(rho P) on the
evolved mixture, which needs the density-matrix backend. A model
carrying readout error is rejected: readout acts on the measurement
record, which no observable sees, and shots on the same model would
disagree by the readout rate.
Sourcepub fn expectation_values_reported(
self,
observables: &[Vec<PauliTerm>],
) -> Result<ExpectationResult>
pub fn expectation_values_reported( self, observables: &[Vec<PauliTerm>], ) -> Result<ExpectationResult>
Simulate::expectation_values with the provenance of the run and, for
a route that estimates rather than evaluates, a standard error per value.
Sourcepub fn observable_expectation(
self,
observable: &PauliObservable,
) -> Result<ObservableExpectation>
pub fn observable_expectation( self, observable: &PauliObservable, ) -> Result<ObservableExpectation>
Compute ⟨H⟩ and its grouped-measurement variance for a weighted
Pauli observable on the circuit’s output state.
The statevector family evaluates one traversal per qubit-wise-commuting
group and reports the variance; see ObservableExpectation::variance
for what the number means. Every other route, including runs with a
noise model or start state attached, evaluates term by term through
Simulate::expectation_values semantics and reports the weighted
mean with no variance. A noise model carrying readout error is rejected
for the same reason as there.
Sourcepub fn observable_variance(
self,
observable: &PauliObservable,
) -> Result<ObservableVariance>
pub fn observable_variance( self, observable: &PauliObservable, ) -> Result<ObservableVariance>
Var(H) = <H^2> - <H>^2 for a weighted Pauli observable on the
circuit’s output state.
This is the spread of the operator itself, the number a shot-based
estimate of <H> converges on dividing by the shot count. It is not
ObservableExpectation::variance, which sums per-group variances and
so drops the covariance between measurement groups.
Evaluates H and the square of its traceless part through
Simulate::observable_expectation, so backend routing, noise, and
start states behave as they do there. The constant term is held out of
the square rather than cancelled inside it; see
PauliObservable::split_identity. The square carries up to T^2
terms over H’s T; see PauliObservable::square.
Sourcepub fn probabilities_of(self, qubits: &[usize]) -> Result<Vec<f64>>
pub fn probabilities_of(self, qubits: &[usize]) -> Result<Vec<f64>>
Joint probability distribution over qubits, 2^k entries with
qubits[0] in the lowest bit.
The subset generalizes Simulate::marginals, which reports each
qubit on its own and so cannot show correlation: a Bell pair reads
(0.5, 0.5) twice there and [0.5, 0, 0, 0.5] here. Routing follows
Simulate::run, including the exact mixture a noise model asks for
and its rejection of readout error, which acts on the measurement
record rather than on the state.
A backend that exposes no distribution for the circuit reports
BackendUnsupported naming itself.
Sourcepub fn state_vector(self) -> Result<Vec<Complex64>>
pub fn state_vector(self) -> Result<Vec<Complex64>>
Full amplitude vector of the circuit’s output state, honoring the selected backend.
Indexed with qubit 0 in the least significant bit, so x q[0] puts the
amplitude at index 1. The circuit must be unitary, for the reason
Simulate::reduced_density_matrix gives.
A noise model declines: a mixture has no single amplitude vector, and
Simulate::reduced_density_matrix over the whole register is the
terminal that answers there. The density-matrix backend declines for
the same reason whether or not noise is attached.
The vector holds 2^n amplitudes, so a register past the dense export
cap reports IncompatibleBackend before allocating rather than after.
Sourcepub fn reduced_density_matrix(
self,
qubits: &[usize],
) -> Result<ReducedDensityMatrix>
pub fn reduced_density_matrix( self, qubits: &[usize], ) -> Result<ReducedDensityMatrix>
Reduced density matrix of qubits on the circuit’s output state,
honoring the selected backend.
Row major with side 2^k; ReducedDensityMatrix::data states the
index order. The subsystem is named once and may be the whole register.
The circuit must be unitary: the answer is read off one state, and a
measurement, reset or conditional leaves one seeded branch of several.
An explicitly selected backend that holds no partial trace reports
BackendUnsupported naming itself. Under BackendKind::Auto a route
that cannot answer falls back to the statevector while the circuit fits
its cap, so the diagnostic is served rather than declined by a choice
the caller did not make. With a noise model attached the answer is the
marginal of the exact mixture, which needs the density-matrix backend.
Sourcepub fn entanglement_entropy(self, subsystem: &[usize]) -> Result<EntropyResult>
pub fn entanglement_entropy(self, subsystem: &[usize]) -> Result<EntropyResult>
Entanglement entropy of subsystem across its cut with the rest of the
register, in nats, honoring the selected backend.
subsystem must leave both sides of the cut non-empty, and the circuit
must be unitary, for the reason Simulate::reduced_density_matrix
gives. The Schmidt values come back with the entropy, descending and
normalized.
A backend that holds the entropy without the spectrum behind it, a
stabilizer cut past the export cap, answers with
EntropyResult::schmidt_values set to None.
An explicitly selected backend that holds neither reports
BackendUnsupported naming itself; under BackendKind::Auto such a
route falls back to the statevector while the circuit fits its cap. A
noise model declines outright: it sends the run to the density matrix,
whose mixed state has no Schmidt decomposition.
Sourcepub fn overlap(self, other: Simulate<'_, Seeded>) -> Result<OverlapResult>
pub fn overlap(self, other: Simulate<'_, Seeded>) -> Result<OverlapResult>
|<a|b>|^2 between this circuit’s output state and other’s, honoring
the backend each side selected.
The two circuits must declare the same width, and both must be unitary
for the reason Simulate::reduced_density_matrix gives. Each side
carries its own backend, seed and start state, and each resolves to a
single backend rather than the decomposed route, since two circuits
need not split into the same independent blocks.
A pair of unlike representations is served by a dense export of both states, so it reaches as far as the export cap does. A pair that shares one answers natively at any width: two chains in the same site order, two tableaux, two product states, or two sparse maps. A noise model on either side is rejected, since the fidelity of two mixtures is not an inner product.
Sourcepub fn expectation_gradient(
self,
hamiltonian: &[(f64, Vec<PauliTerm>)],
params: &Parameters,
) -> Result<ExpectationGradient>
pub fn expectation_gradient( self, hamiltonian: &[(f64, Vec<PauliTerm>)], params: &Parameters, ) -> Result<ExpectationGradient>
Compute ⟨H⟩ and its exact gradient with respect to the bound
parameters using the adjoint method.
hamiltonian is a weighted Pauli sum Σ c_k P_k with real
coefficients. params declares which gate instructions carry parameters.
Runs on the statevector backend; the selected backend must be Auto or
Statevector. The circuit must be unitary. See
gradient::run_expectation_gradient.
Sourcepub fn expectation_gradient_shift(
self,
hamiltonian: &[(f64, Vec<PauliTerm>)],
params: &Parameters,
) -> Result<ExpectationGradient>
pub fn expectation_gradient_shift( self, hamiltonian: &[(f64, Vec<PauliTerm>)], params: &Parameters, ) -> Result<ExpectationGradient>
Compute ⟨H⟩ and its gradient by the parameter-shift rule on the
selected backend.
Serves the cases Simulate::expectation_gradient declines: any
backend with a native observable path, circuits containing QftBlock,
widths past the statevector cap, and a noise model. It differentiates the
same gate set (Rx, Ry, Rz, Rzz, P, PauliRot) at 1 + 2 * links
circuit evaluations against the adjoint’s one, so the adjoint stays the
better choice where it applies. A backend with no native observable path
reports BackendUnsupported naming itself. Under a noise model every
evaluation reads the exact mixture, so the backend must be
BackendKind::DensityMatrix or its device sibling; the shift stays
exact because the channels do not depend on the shifted angle. See
gradient::run_expectation_gradient_shift.
Auto Trait Implementations§
impl<'c, SeedState> !RefUnwindSafe for Simulate<'c, SeedState>
impl<'c, SeedState> !UnwindSafe for Simulate<'c, SeedState>
impl<'c, SeedState> Freeze for Simulate<'c, SeedState>where
SeedState: Freeze,
impl<'c, SeedState> Send for Simulate<'c, SeedState>where
SeedState: Send,
impl<'c, SeedState> Sync for Simulate<'c, SeedState>where
SeedState: Sync,
impl<'c, SeedState> Unpin for Simulate<'c, SeedState>where
SeedState: Unpin,
impl<'c, SeedState> UnsafeUnpin for Simulate<'c, SeedState>where
SeedState: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T, U> Imply<T> for U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more