#[derive(Debug, Clone)]
pub struct ExpectationResult {
pub values: Vec<f64>,
pub std_errors: Option<Vec<f64>>,
pub metadata: RunMetadata,
}
impl ExpectationResult {
pub fn into_values(self) -> Vec<f64> {
self.values
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolvedBackend {
ProductState,
Stabilizer,
FactoredStabilizer,
Sparse,
Mps,
Factored,
TensorNetwork,
Statevector,
DensityMatrix,
Distributed,
StabilizerRank,
StochasticPauli,
DeterministicPauli,
PauliPath,
CompiledStabilizer,
Decomposed,
Other(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Engine {
CompiledSampler,
NoisyCompiledSampler,
FrameSampler,
HomologicalSampler,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Exactness {
Exact,
Approximate {
fidelity_lower_bound: Option<f64>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Placement {
Host,
Device,
}
#[derive(Debug, Clone)]
pub struct RunMetadata {
pub backend: ResolvedBackend,
pub engine: Option<Engine>,
pub exactness: Exactness,
pub placement: Placement,
pub shots: Option<usize>,
}
impl RunMetadata {
pub(crate) fn new(
backend: ResolvedBackend,
exactness: Exactness,
placement: Placement,
) -> Self {
Self {
backend,
engine: None,
exactness,
placement,
shots: None,
}
}
pub(crate) fn exact(backend: ResolvedBackend) -> Self {
Self::new(backend, Exactness::Exact, Placement::Host)
}
pub(crate) fn approximate(backend: ResolvedBackend) -> Self {
Self::new(
backend,
Exactness::Approximate {
fidelity_lower_bound: None,
},
Placement::Host,
)
}
pub(crate) fn with_shots(mut self, shots: usize) -> Self {
self.shots = Some(shots);
self
}
pub(crate) fn with_engine(mut self, engine: Engine) -> Self {
self.engine = Some(engine);
self
}
pub fn is_exact(&self) -> bool {
matches!(self.exactness, Exactness::Exact)
}
pub fn fidelity_lower_bound(&self) -> Option<f64> {
match self.exactness {
Exactness::Exact => None,
Exactness::Approximate {
fidelity_lower_bound,
} => fidelity_lower_bound,
}
}
pub(crate) fn weaken_with(&mut self, other: &RunMetadata) {
if other.placement != self.placement {
self.placement = Placement::Host;
}
match (self.exactness, other.exactness) {
(_, Exactness::Exact) => {}
(Exactness::Exact, approx) => self.exactness = approx,
(
Exactness::Approximate {
fidelity_lower_bound: a,
},
Exactness::Approximate {
fidelity_lower_bound: b,
},
) => {
self.exactness = Exactness::Approximate {
fidelity_lower_bound: match (a, b) {
(Some(a), Some(b)) => Some(a.min(b)),
_ => None,
},
};
}
}
}
pub(crate) fn decomposed(parts: impl IntoIterator<Item = RunMetadata>) -> Self {
let mut exact = true;
let mut bound = Some(1.0f64);
let mut all_device = true;
for part in parts {
all_device &= part.placement == Placement::Device;
if let Exactness::Approximate {
fidelity_lower_bound,
} = part.exactness
{
exact = false;
bound = match (bound, fidelity_lower_bound) {
(Some(acc), Some(b)) => Some(acc * b),
_ => None,
};
}
}
let exactness = if exact {
Exactness::Exact
} else {
Exactness::Approximate {
fidelity_lower_bound: bound,
}
};
let placement = if all_device {
Placement::Device
} else {
Placement::Host
};
Self::new(ResolvedBackend::Decomposed, exactness, placement)
}
}