use super::builtin::{
or_operand_error, And, ApproxTopN, CoefficientThreshold, CollapseSample, Or, TopN, WeightCutoff,
};
use super::TruncationPolicy;
use crate::collectives::Collectives;
use crate::pauli_sum::PauliSum;
use num_complex::Complex64;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq)]
pub enum BuiltinTruncation {
Keep,
Coefficient(f64),
Weight(u32),
TopN(usize),
ApproxTopN(usize),
CollapseSample(Arc<CollapseSample>),
And(Box<BuiltinTruncation>, Box<BuiltinTruncation>),
Or(Box<BuiltinTruncation>, Box<BuiltinTruncation>),
}
impl BuiltinTruncation {
pub fn or(first: BuiltinTruncation, second: BuiltinTruncation) -> Self {
Self::try_or(first, second).unwrap_or_else(|message| panic!("{message}"))
}
pub fn try_or(first: BuiltinTruncation, second: BuiltinTruncation) -> Result<Self, String> {
match or_operand_error(first.has_layer_pass(), second.has_layer_pass()) {
Some(message) => Err(message),
None => Ok(Self::Or(Box::new(first), Box::new(second))),
}
}
fn has_layer_pass(&self) -> bool {
match self {
Self::TopN(_) | Self::ApproxTopN(_) | Self::CollapseSample(_) => true,
Self::And(a, b) => a.has_layer_pass() || b.has_layer_pass(),
Self::Or(a, b) => {
if let Some(message) = or_operand_error(a.has_layer_pass(), b.has_layer_pass()) {
panic!("{message}");
}
false
}
Self::Keep | Self::Coefficient(_) | Self::Weight(_) => false,
}
}
pub fn supports_partitioned(&self) -> bool {
match self {
Self::TopN(_) => false,
Self::And(a, b) => a.supports_partitioned() && b.supports_partitioned(),
Self::Or(..) => !self.has_layer_pass(),
Self::Keep
| Self::Coefficient(_)
| Self::Weight(_)
| Self::ApproxTopN(_)
| Self::CollapseSample(_) => true,
}
}
pub fn contains_collapse_sample(&self) -> bool {
match self {
Self::CollapseSample(_) => true,
Self::And(a, b) | Self::Or(a, b) => {
a.contains_collapse_sample() || b.contains_collapse_sample()
}
Self::Keep
| Self::Coefficient(_)
| Self::Weight(_)
| Self::TopN(_)
| Self::ApproxTopN(_) => false,
}
}
}
impl<const W: usize> TruncationPolicy<W> for BuiltinTruncation {
fn keep_term(&self, x: &[u64; W], z: &[u64; W], c: Complex64) -> bool {
match self {
Self::Keep => true,
Self::Coefficient(eps) => CoefficientThreshold(*eps).keep_term(x, z, c),
Self::Weight(k) => WeightCutoff(*k).keep_term(x, z, c),
Self::TopN(n) => <TopN as TruncationPolicy<W>>::keep_term(&TopN(*n), x, z, c),
Self::ApproxTopN(n) => {
<ApproxTopN as TruncationPolicy<W>>::keep_term(&ApproxTopN(*n), x, z, c)
}
Self::CollapseSample(s) => {
<CollapseSample as TruncationPolicy<W>>::keep_term(s, x, z, c)
}
Self::And(a, b) => a.keep_term(x, z, c) && b.keep_term(x, z, c),
Self::Or(a, b) => a.keep_term(x, z, c) || b.keep_term(x, z, c),
}
}
fn finalize_layer(&self, sum: &mut PauliSum<W>) {
match self {
Self::TopN(n) => TopN(*n).finalize_layer(sum),
Self::ApproxTopN(n) => ApproxTopN(*n).finalize_layer(sum),
Self::CollapseSample(s) => {
<CollapseSample as TruncationPolicy<W>>::finalize_layer(s, sum)
}
Self::And(a, b) => {
a.finalize_layer(sum);
b.finalize_layer(sum);
}
Self::Keep | Self::Coefficient(_) | Self::Weight(_) | Self::Or(_, _) => {}
}
}
fn finalizes_layer(&self) -> bool {
self.has_layer_pass()
}
fn finalize_layer_partitioned(&self, local: &mut PauliSum<W>, collectives: &dyn Collectives) {
match self {
Self::TopN(n) => <TopN as TruncationPolicy<W>>::finalize_layer_partitioned(
&TopN(*n),
local,
collectives,
),
Self::ApproxTopN(n) => <ApproxTopN as TruncationPolicy<W>>::finalize_layer_partitioned(
&ApproxTopN(*n),
local,
collectives,
),
Self::CollapseSample(s) => {
<CollapseSample as TruncationPolicy<W>>::finalize_layer_partitioned(
s,
local,
collectives,
)
}
Self::And(a, b) => {
<Self as TruncationPolicy<W>>::finalize_layer_partitioned(a, local, collectives);
<Self as TruncationPolicy<W>>::finalize_layer_partitioned(b, local, collectives);
}
Self::Keep | Self::Coefficient(_) | Self::Weight(_) | Self::Or(_, _) => {}
}
}
fn supports_partitioned(&self) -> bool {
BuiltinTruncation::supports_partitioned(self)
}
}
impl From<CoefficientThreshold> for BuiltinTruncation {
fn from(p: CoefficientThreshold) -> Self {
Self::Coefficient(p.0)
}
}
impl From<WeightCutoff> for BuiltinTruncation {
fn from(p: WeightCutoff) -> Self {
Self::Weight(p.0)
}
}
impl From<TopN> for BuiltinTruncation {
fn from(p: TopN) -> Self {
Self::TopN(p.0)
}
}
impl From<ApproxTopN> for BuiltinTruncation {
fn from(p: ApproxTopN) -> Self {
Self::ApproxTopN(p.0)
}
}
impl From<CollapseSample> for BuiltinTruncation {
fn from(p: CollapseSample) -> Self {
Self::CollapseSample(Arc::new(p))
}
}
impl From<Arc<CollapseSample>> for BuiltinTruncation {
fn from(p: Arc<CollapseSample>) -> Self {
Self::CollapseSample(p)
}
}
impl<A: Into<BuiltinTruncation>, B: Into<BuiltinTruncation>> From<And<A, B>> for BuiltinTruncation {
fn from(p: And<A, B>) -> Self {
Self::And(Box::new(p.0.into()), Box::new(p.1.into()))
}
}
impl<A: Into<BuiltinTruncation>, B: Into<BuiltinTruncation>> From<Or<A, B>> for BuiltinTruncation {
fn from(p: Or<A, B>) -> Self {
Self::or(p.0.into(), p.1.into())
}
}
impl<P: Clone + Into<BuiltinTruncation>> From<&P> for BuiltinTruncation {
fn from(p: &P) -> Self {
p.clone().into()
}
}
#[cfg(test)]
mod tests;