pub mod density_matrix;
#[cfg(feature = "distributed")]
pub mod distributed_statevector;
pub mod factored;
pub mod factored_stabilizer;
pub(crate) mod memory;
pub mod mps;
pub mod product;
pub(crate) mod simd;
pub mod sparse;
pub mod stabilizer;
pub mod statevector;
pub mod tensornetwork;
pub(crate) mod word_ops;
use num_complex::Complex64;
use crate::circuit::Instruction;
use crate::error::Result;
use crate::sim::unified_pauli::PauliTerm;
pub(crate) const PARALLEL_THRESHOLD_QUBITS: usize = 14;
#[cfg(feature = "parallel")]
pub(crate) const MIN_PAR_ELEMS: usize = 4096;
#[cfg(feature = "parallel")]
#[inline(always)]
pub(crate) fn chunk_min_len(chunk_size: usize) -> usize {
(MIN_PAR_ELEMS / chunk_size).max(1)
}
#[cfg(feature = "parallel")]
pub(crate) const MIN_PAR_ITERS: usize = 2048;
#[cfg(feature = "parallel")]
pub(crate) const MIN_PAR_REDUCE_ELEMS: usize = 1 << 16;
pub(crate) fn state_norm_sqr(state: &[Complex64]) -> f64 {
#[cfg(feature = "parallel")]
if state.len() >= MIN_PAR_REDUCE_ELEMS {
use rayon::prelude::*;
return state
.par_chunks(MIN_PAR_ELEMS)
.map(simd::norm_sqr_sum)
.sum();
}
simd::norm_sqr_sum(state)
}
#[cfg(test)]
mod norm_tests {
use super::state_norm_sqr;
use num_complex::Complex64;
#[test]
fn state_norm_sqr_matches_scalar_sum_across_the_parallel_threshold() {
for len in [1usize, 3, 4096, (1 << 16) - 1, 1 << 16, (1 << 17) + 5] {
let state: Vec<Complex64> = (0..len)
.map(|i| Complex64::new(0.001 * i as f64 - 0.5, 0.002 * i as f64 + 0.25))
.collect();
let scalar: f64 = state.iter().map(Complex64::norm_sqr).sum();
let got = state_norm_sqr(&state);
assert!(
(got - scalar).abs() <= 1e-9 * scalar.max(1.0),
"len {len}: expected {scalar}, got {got}"
);
}
}
}
#[cfg(feature = "parallel")]
pub(crate) const MIN_QUBITS_FOR_PAR_GATES: usize = 128;
#[cfg(feature = "parallel")]
pub(crate) const MIN_ANTI_ROWS_FOR_PAR: usize = 4;
pub(crate) const NORM_CLAMP_MIN: f64 = 1e-30;
pub(crate) const PHASE_IS_ONE_EPS: f64 = 1e-15;
pub(crate) use memory::{
DM_QUBIT_CAP_ENV, check_state_allocation, dense_probability_len, dense_statevector_len,
max_dense_outcome_bits, max_density_matrix_qubits, max_statevector_qubits,
reserve_dense_output, tensor_probability_len,
};
#[inline(always)]
pub(crate) fn is_phase_one(phase: Complex64) -> bool {
(phase.re - 1.0).abs() < PHASE_IS_ONE_EPS && phase.im.abs() < PHASE_IS_ONE_EPS
}
#[inline(always)]
pub(crate) fn measurement_inv_norm(outcome: bool, prob_one: f64) -> f64 {
let prob_outcome = if outcome { prob_one } else { 1.0 - prob_one };
1.0 / prob_outcome.clamp(NORM_CLAMP_MIN, 1.0).sqrt()
}
#[inline(always)]
pub(crate) fn init_classical_bits(bits: &mut Vec<bool>, num: usize) {
if bits.len() == num {
bits.fill(false);
} else {
*bits = vec![false; num];
}
}
#[cfg(feature = "parallel")]
pub(crate) fn init_thread_pool() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
if std::env::var("RAYON_NUM_THREADS").is_err() {
let threads = num_cpus::get();
rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build_global()
.ok();
}
});
}
#[inline(always)]
pub(crate) fn sorted_mcu_qubits(controls: &[usize], target: usize, buf: &mut [usize; 10]) -> usize {
let n = controls.len() + 1;
buf[..controls.len()].copy_from_slice(controls);
buf[controls.len()] = target;
buf[..n].sort_unstable();
n
}
#[derive(Debug, Clone)]
pub struct BasisSamples {
words: Vec<u64>,
words_per_shot: usize,
}
impl BasisSamples {
pub(crate) fn new(num_shots: usize, num_qubits: usize) -> Self {
let words_per_shot = num_qubits.div_ceil(64).max(1);
Self {
words: vec![0u64; num_shots * words_per_shot],
words_per_shot,
}
}
#[inline(always)]
pub(crate) fn set(&mut self, shot: usize, qubit: usize) {
self.words[shot * self.words_per_shot + qubit / 64] |= 1u64 << (qubit % 64);
}
#[inline(always)]
pub(crate) fn set_index(&mut self, shot: usize, index: usize) {
self.words[shot * self.words_per_shot] = index as u64;
}
pub fn num_shots(&self) -> usize {
self.words.len() / self.words_per_shot
}
#[inline(always)]
pub fn bit(&self, shot: usize, qubit: usize) -> bool {
let word = self.words[shot * self.words_per_shot + qubit / 64];
(word >> (qubit % 64)) & 1 == 1
}
}
pub trait Backend {
fn name(&self) -> &'static str;
fn init(&mut self, num_qubits: usize, num_classical_bits: usize) -> Result<()>;
fn apply(&mut self, instruction: &Instruction) -> Result<()>;
fn classical_results(&self) -> &[bool];
fn probabilities(&self) -> Result<Vec<f64>>;
fn num_qubits(&self) -> usize;
fn apply_instructions(&mut self, instructions: &[Instruction]) -> Result<()> {
for instruction in instructions {
self.apply(instruction)?;
}
Ok(())
}
fn supports_fused_gates(&self) -> bool {
true
}
fn supports_qft_block(&self) -> bool {
false
}
fn export_statevector(&self) -> Result<Vec<Complex64>> {
Err(crate::error::PrismError::BackendUnsupported {
backend: self.name().to_string(),
operation: "statevector export".to_string(),
})
}
fn qubit_probability(&self, qubit: usize) -> Result<f64> {
let rho = self.reduced_density_matrix_1q(qubit)?;
Ok(rho[1][1].re.clamp(0.0, 1.0))
}
fn reduced_density_matrix_1q(&self, _qubit: usize) -> Result<[[Complex64; 2]; 2]> {
Err(crate::error::PrismError::BackendUnsupported {
backend: self.name().to_string(),
operation: "reduced_density_matrix_1q".to_string(),
})
}
fn reset(&mut self, _qubit: usize) -> Result<()> {
Err(crate::error::PrismError::BackendUnsupported {
backend: self.name().to_string(),
operation: "reset".to_string(),
})
}
fn supports_native_sampling(&self) -> bool {
false
}
fn sample_basis_states(&self, _num_shots: usize, _seed: u64) -> Result<BasisSamples> {
Err(crate::error::PrismError::BackendUnsupported {
backend: self.name().to_string(),
operation: "native basis-state sampling".to_string(),
})
}
fn supports_pauli_expectation(&self) -> bool {
false
}
fn pauli_expectations(&self, _observables: &[Vec<PauliTerm>]) -> Result<Vec<f64>> {
Err(crate::error::PrismError::BackendUnsupported {
backend: self.name().to_string(),
operation: "Pauli expectation values".to_string(),
})
}
fn apply_1q_matrix(&mut self, qubit: usize, matrix: &[[Complex64; 2]; 2]) -> Result<()> {
use crate::circuit::smallvec;
self.apply(&crate::circuit::Instruction::Gate {
gate: crate::gates::Gate::Fused(Box::new(*matrix)),
targets: smallvec![qubit],
})
}
}