Skip to main content

DensityMatrixBackend

Struct DensityMatrixBackend 

Source
pub struct DensityMatrixBackend { /* private fields */ }
Expand description

Exact density-matrix simulator. See the module docs for the state layout.

Implementations§

Source§

impl DensityMatrixBackend

Source

pub fn new(seed: u64) -> Self

Source

pub fn with_gpu(self, context: Arc<GpuContext>) -> Self

Available on crate feature gpu only.

Hold the mixture on the device bound to context.

Backend::init then allocates the 4^n buffer in device memory after a budget check against the free VRAM, and every sweep runs as a kernel. A device that cannot hold the state is an error at init, never a host fallback. The channel entry points keep their infallible signatures, so a kernel launch that fails under one of them panics.

Source

pub fn density_matrix(&self) -> Result<Vec<Complex64>>

The full 4^n buffer, row-major as the module docs lay it out. Reads back from the device when the mixture is resident there.

Source

pub fn purity(&self) -> f64

Purity Tr(rho^2), equal to 1 for a pure state and less otherwise.

Source

pub fn apply_1q_kraus(&mut self, qubit: usize, kraus: &[[[Complex64; 2]; 2]])

Apply a general single-qubit channel rho -> sum_k K_k rho K_k^dagger on qubit. The Kraus set is compiled once into a 4x4 block superoperator acting on each (row-bit, col-bit) block of rho, so the buffer is swept once with no per-element allocation.

Source

pub fn apply_2q_depolarizing(&mut self, q0: usize, q1: usize, p: f64)

Apply symmetric two-qubit depolarizing on (q0, q1): rho -> (1-p) rho + (p/15) sum_{P != I(x)I} P rho P, summed over the 15 non-identity two-qubit Paulis.

The Pauli twirl sum_P P B P = 4 Tr(B) I4 over all 16 two-qubit Paulis holds for any 4x4 B, so on each (q0, q1) block the map collapses to B -> alpha B + beta Tr(B) I4 with alpha = 1 - 16p/15 and beta = 4p/15. That is one real scale per amplitude against the 16 complex multiply-accumulates the equivalent 16x16 superoperator pays in DensityMatrixBackend::apply_2q_kraus.

§Panics

If q0 and q1 are equal or outside the register, as DensityMatrixBackend::apply_2q_kraus does.

Source

pub fn apply_2q_kraus( &mut self, q0: usize, q1: usize, kraus: &[[[Complex64; 4]; 4]], )

Apply a general two-qubit channel rho -> sum_k K_k rho K_k^dagger on (q0, q1). Each operator is indexed K[t][t'] with t = 2 * bit(q0) + bit(q1), matching Gate::matrix_4x4.

The Kraus set is compiled once into a 16x16 block superoperator over the four-bit (q0-row, q1-row, q0-col, q1-col) block, so the buffer is swept once. Block index 4*tr + tc orders the two-qubit row value tr against the column value tc. An all-diagonal set compiles to a diagonal superoperator and is applied as one complex multiply per amplitude in a contiguous pass instead of the dense sweep. On hosts with detected AVX2 and FMA the dense sweep issues each row inner product two complex terms per FMA through simd::PreparedKraus2q.

§Panics

If q0 and q1 are equal or outside the register. A model built through NoiseModel is rejected before it reaches here.

Source

pub fn expectations_pauli(&self, masks: &[(usize, usize, u32)]) -> Vec<f64>

Exact Tr(rho P) for every joint Pauli reduced to (xmask, zmask, num_y), where P|j> = i^{num_y} * (-1)^{popcount(j & zmask)} * |j ^ xmask>. The trace collapses to a diagonal-offset sum: Tr(rho P) = i^{num_y} sum_j (-1)^{popcount(j & zmask)} rho[j][j ^ xmask].

Each row of the 4^n buffer is visited once and contributes one entry per observable, so the strided sweep is paid once instead of once per observable.

Trait Implementations§

Source§

impl Backend for DensityMatrixBackend

Source§

fn overlap_sq(&self, _other: &dyn Backend) -> Result<f64>

Declined: the fidelity of two mixtures is not an inner product, and a mixture holds no statevector for the dense route to export.

Source§

fn reduced_density_matrix( &mut self, subsystem: &[usize], ) -> Result<Vec<Complex64>>

Partial trace of the 4^n buffer, 2^(n + k) reads. At k = n the buffer itself comes back, its rows and columns in subsystem order.

Source§

fn init(&mut self, num_qubits: usize, num_classical_bits: usize) -> Result<()>

With a device attached the host cap does not apply: the mixture is budgeted against free VRAM instead, before anything is allocated.

Source§

fn init_from_amplitudes( &mut self, amplitudes: Vec<Complex64>, num_classical_bits: usize, ) -> Result<()>

Starts from the pure mixture |psi><psi|, so the buffer is the 4^n outer product of amplitudes with its own conjugate. The cap check in init runs first, before that buffer is sized.

Source§

fn supports_fused_gates(&self) -> bool

MultiFused and Multi2q apply their constituents one at a time here rather than through the tiled kernels, which the ket register’s shifted indices would reorder.

Source§

fn fusion_state_qubits(&self, num_qubits: usize) -> usize

The mixture is a 2n-qubit statevector, so every fusion floor is reached at half the circuit width the statevector needs.

Source§

fn pauli_expectations(&self, observables: &[Vec<PauliTerm>]) -> Result<Vec<f64>>

Tr(rho P_k) per observable, the mixed-state reading of the trait’s <psi|P_k|psi>. rho is trace-one by construction, so no normalization divide is needed.

Source§

fn reduced_density_matrix_1q(&self, qubit: usize) -> Result<[[Complex64; 2]; 2]>

On the device the four entries come from the Pauli sums T, Z, X, and Y on qubit: the diagonal is (T +- Z) / 2 and the off-diagonal pair is (X +- Y) / 2, where Y carries the row sign and no i.

Source§

fn apply_1q_matrix( &mut self, qubit: usize, matrix: &[[Complex64; 2]; 2], ) -> Result<()>

Evolve rho -> K rho K^dagger for an arbitrary K, on the same kernel selection as the one-qubit branch of apply_unitary. Trajectories never route here, since supports_noisy_per_shot excludes the density matrix in favour of apply_1q_kraus on the mixture; this keeps the trait method allocation-free on every backend that holds a state.

Source§

fn name(&self) -> &'static str

Human-readable backend name (for error messages, logging, and benchmarks).
Source§

fn as_any(&self) -> Option<&dyn Any>

The concrete backend behind a &dyn Backend, so Backend::overlap_sq can recognize its own representation on the other side of the inner product. An implementor that wants the fast paths writes Some(self); the default hides the concrete type, which costs only the dense route.
Source§

fn resolved(&self) -> ResolvedBackend

Which engine this is, for the provenance attached to every result. The default names an out-of-tree backend by its Backend::name.
Source§

fn schmidt_values(&mut self, _subsystem: &[usize]) -> Result<Vec<f64>>

Schmidt values of the state across the cut between subsystem and its complement: descending, numerically zero values dropped, squares summing to 1 whatever norm the representation carries. Read more
Source§

fn placement(&self) -> Placement

Where the state lived during the run. Only the statevector has a device path, and only when the gpu feature is on.
Source§

fn supports_initial_state(&self) -> bool

Whether Backend::init_from_amplitudes can start this backend from a caller-supplied state.
Source§

fn apply(&mut self, instruction: &Instruction) -> Result<()>

Apply a single instruction to the current state. Read more
Source§

fn classical_results(&self) -> &[bool]

Read classical measurement results. Read more
Source§

fn probabilities(&self) -> Result<Vec<f64>>

Compute the probability of each computational basis state. Read more
Source§

fn num_qubits(&self) -> usize

Number of qubits the backend is currently configured for.
Source§

fn qubit_probability(&self, qubit: usize) -> Result<f64>

Compute P(qubit = |1⟩) without collapsing the state. Read more
Source§

fn reset(&mut self, qubit: usize) -> Result<()>

Reset a qubit to |0⟩, discarding any prior amplitude on that qubit. Read more
Source§

fn supports_pauli_expectation(&self) -> bool

Whether Backend::pauli_expectations evaluates observables on this backend’s own representation.
Source§

fn exactness(&self) -> Exactness

Whether this representation can discard state weight, and how much it discarded on the run just executed. Called once per run, after the circuit has been applied; the default reports an exact representation.
Source§

fn block_probabilities(&self) -> Option<Probabilities>

Per-block probabilities for a backend holding a product of independent sub-states, skipping the 2^n Kronecker expansion Backend::probabilities would materialize. Read more
Source§

fn apply_instructions(&mut self, instructions: &[Instruction]) -> Result<()>

Apply a batch of instructions to the current state. Read more
Source§

fn apply_region(&mut self, region: &GuardedRegion) -> Result<()>

Execute a guarded region’s body iff its condition holds. Read more
Source§

fn supports_qft_block(&self) -> bool

Whether this backend has a native kernel for Gate::QftBlock. Read more
Source§

fn supports_pauli_rotation(&self) -> bool

Whether this backend has a native kernel for Gate::PauliRot. Read more
Source§

fn export_statevector(&self) -> Result<Vec<Complex64>>

Export the current quantum state as a dense statevector. Read more
Source§

fn supports_two_qubit_kraus(&self) -> bool

Whether this backend can run a NoiseChannel::Kraus2q branch, which needs both Backend::reduced_density_matrix_2q and a Gate::Fused2q kernel. Checked before a shot starts, so an incapable backend is named at dispatch rather than part way through a trajectory.
Source§

fn reduced_density_matrix_2q( &self, _q0: usize, _q1: usize, ) -> Result<[[Complex64; 4]; 4]>

Compute the two-qubit reduced density matrix without collapsing the state. Read more
Source§

fn supports_native_sampling(&self) -> bool

Whether Backend::sample_basis_states draws from this backend’s own representation. Read more
Source§

fn sample_basis_states( &mut self, _num_shots: usize, _seed: u64, ) -> Result<BasisSamples>

Draw num_shots computational-basis outcomes from the current state. Read more
Source§

fn entanglement_entropy(&mut self, subsystem: &[usize]) -> Result<f64>

Entanglement entropy of subsystem in nats: -sum p ln p over p = s^2 / sum s^2 for the Backend::schmidt_values s, which is where the default reads it from.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V