deep_causality_quantum 0.2.5

Quantum causal models on the causal monad: quantum-information kernels, gates, and the operator layer for DeepCausality.
Documentation
/*
 * SPDX-License-Identifier: MIT
 * Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
 */

use alloc::format;
use alloc::string::String;

use core::fmt::{Debug, Display, Formatter};
use deep_causality_core::{CausalityError, CausalityErrorEnum};

/// The crate-local quantum error: an outer newtype over [`QuantumErrorEnum`],
/// mirroring the repo convention (`CausalityError(CausalityErrorEnum::…)`).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct QuantumError(pub QuantumErrorEnum);

/// Detailed classification of quantum errors. Typed variants name the exact
/// failure; a `String` payload carries the operation-specific context.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum QuantumErrorEnum {
    /// Operations attempted on states or operators with incompatible dimensions/shapes.
    DimensionMismatch(String),
    /// The operands carry different Clifford metric signatures.
    MetricMismatch(String),
    /// The Clifford metric is unsupported for the requested operation
    /// (e.g. an odd-dimensional metric for the ket↔matrix bridge), or a
    /// metric convention error surfaced from `deep_causality_metric`.
    UnsupportedMetric(String),
    /// A non-finite value (NaN, ±inf) was produced or encountered.
    NonFiniteValue(String),
    /// Probability normalization failed (value < 0, > 1, or sum ≠ 1).
    NormalizationError(String),
    /// An operator required to be positive (semi-)definite is not.
    NonPositiveOperator(String),
    /// A density or Choi–Jamiołkowski operator does not have the required trace.
    NonUnitTrace(String),
    /// A channel is not completely positive and trace-preserving.
    NonCptpChannel(String),
    /// A partial trace was requested with an inconsistent subsystem shape.
    PartialTraceShape(String),
    /// The freeze-time quantum Markov check found a non-commuting factor pair;
    /// `node_j`/`node_k` name the offending operators by graph node index.
    CommutatorNonZero {
        node_j: usize,
        node_k: usize,
        detail: String,
    },
    /// The declared causal structure contains a `C₃` sub-relation and therefore
    /// does not imply a unitary causally faithful decomposition in the
    /// traditional circuit paradigm (van der Lugt & Lorenz, arXiv:2508.11762,
    /// Definition 3.1 and Theorem 3.2).
    ///
    /// "Faithfully" is the Lorenz–Barrett sense: a circuit decomposition whose
    /// connectivity equals the unitary's causal structure, `G_U = G_C`. It is not
    /// Pearl's faithfulness, where a distribution has no independences beyond
    /// those its graph implies. The structure is what is rejected; a particular
    /// unitary with that structure may still decompose faithfully (Remark 3.3),
    /// and every such unitary has a routed decomposition.
    NotFaithfullyRepresentable(String),
    /// A Markov re-check on a composite's *inherited* factors found a
    /// non-commuting pair. This is a failure of the certificate, not of the
    /// model: Barrett–Lorenz–Oreshkov's representation theorem gives every
    /// composite of QCM-representable parts a Markov factorization for the
    /// induced DAG with the induced factors, and the naive product of the
    /// parts' factors need not be it. `CommutatorNonZero` is reserved for
    /// factors that are the model's own.
    CertificateNotInherited {
        node_j: usize,
        node_k: usize,
        detail: String,
    },
    /// A structural candidate's causal structure contains a directed cycle.
    /// Cyclic quantum causal models exist (Barrett, Lorenz & Oreshkov,
    /// arXiv:2002.12157) and the C₃ criterion does not reject them, so this is
    /// a scope decision made at `build()`, before any check runs, and it names
    /// the limit rather than an obstruction.
    CyclicStructureUnsupported(String),
    /// `design` was asked to cover more hypotheses than its cap. The exact
    /// cover is a dynamic program over `2^C(n,2)` subsets of pairs: `2^15` at
    /// n = 6, `2^28` at n = 8, `2^45` at n = 10. Above `max_hypotheses` the
    /// solve is refused before the table is allocated, naming `n` and the
    /// pair count. A later version may supply the greedy cover with its
    /// logarithmic approximation factor reported; v1 does not.
    HypothesisCountExceeded { n: usize, pairs: usize },
    /// A marginalisation was refused because its boundary warrant did not
    /// hold: the kept-factor operator `Z ⊗ 1_B` fails to commute with the
    /// operator being traced within the named tolerance, so nothing may be
    /// asserted about the traced commutator and no traced operator is
    /// produced. The message carries the residual, the tolerance and the
    /// amplification; `Hypothesis::boundary_warrant` returns them typed.
    BoundaryNotHeld(String),
    /// A Pauli handed to the logical-equivalence predicate lies outside the
    /// code's normalizer: it anticommutes with the stabilizer generator named by
    /// `generator`, so it does not preserve the code space and the question of
    /// whether it acts trivially there is not well-posed. `detail` says which
    /// kind of generator, `Z` or `X`.
    NotInNormalizer { generator: usize, detail: String },
    /// A gate in a program handed to the Clifford tableau is not Clifford, so
    /// its conjugation action on a Pauli is not a symplectic update and the
    /// program cannot be pushed through. Names the gate and its position.
    NonCliffordGate(String),
    /// Numerical conversion or general calculation failure.
    CalculationError(String),
}

impl QuantumError {
    pub(crate) fn new(variant: QuantumErrorEnum) -> Self {
        Self(variant)
    }

    #[allow(non_snake_case)]
    pub fn DimensionMismatch(msg: String) -> Self {
        Self(QuantumErrorEnum::DimensionMismatch(msg))
    }

    #[allow(non_snake_case)]
    pub fn MetricMismatch(msg: String) -> Self {
        Self(QuantumErrorEnum::MetricMismatch(msg))
    }

    #[allow(non_snake_case)]
    pub fn UnsupportedMetric(msg: String) -> Self {
        Self(QuantumErrorEnum::UnsupportedMetric(msg))
    }

    #[allow(non_snake_case)]
    pub fn NonFiniteValue(msg: String) -> Self {
        Self(QuantumErrorEnum::NonFiniteValue(msg))
    }

    #[allow(non_snake_case)]
    pub fn NormalizationError(msg: String) -> Self {
        Self(QuantumErrorEnum::NormalizationError(msg))
    }

    #[allow(non_snake_case)]
    pub fn NonPositiveOperator(msg: String) -> Self {
        Self(QuantumErrorEnum::NonPositiveOperator(msg))
    }

    #[allow(non_snake_case)]
    pub fn NonUnitTrace(msg: String) -> Self {
        Self(QuantumErrorEnum::NonUnitTrace(msg))
    }

    #[allow(non_snake_case)]
    pub fn NonCptpChannel(msg: String) -> Self {
        Self(QuantumErrorEnum::NonCptpChannel(msg))
    }

    #[allow(non_snake_case)]
    pub fn PartialTraceShape(msg: String) -> Self {
        Self(QuantumErrorEnum::PartialTraceShape(msg))
    }

    #[allow(non_snake_case)]
    pub fn CommutatorNonZero(node_j: usize, node_k: usize, detail: String) -> Self {
        Self(QuantumErrorEnum::CommutatorNonZero {
            node_j,
            node_k,
            detail,
        })
    }

    #[allow(non_snake_case)]
    pub fn NotFaithfullyRepresentable(msg: String) -> Self {
        Self(QuantumErrorEnum::NotFaithfullyRepresentable(msg))
    }

    #[allow(non_snake_case)]
    pub fn CertificateNotInherited(node_j: usize, node_k: usize, detail: String) -> Self {
        Self(QuantumErrorEnum::CertificateNotInherited {
            node_j,
            node_k,
            detail,
        })
    }

    #[allow(non_snake_case)]
    pub fn CyclicStructureUnsupported(msg: String) -> Self {
        Self(QuantumErrorEnum::CyclicStructureUnsupported(msg))
    }

    #[allow(non_snake_case)]
    pub fn HypothesisCountExceeded(n: usize, pairs: usize) -> Self {
        Self(QuantumErrorEnum::HypothesisCountExceeded { n, pairs })
    }

    #[allow(non_snake_case)]
    pub fn BoundaryNotHeld(msg: String) -> Self {
        Self(QuantumErrorEnum::BoundaryNotHeld(msg))
    }

    #[allow(non_snake_case)]
    pub fn NotInNormalizer(generator: usize, detail: String) -> Self {
        Self(QuantumErrorEnum::NotInNormalizer { generator, detail })
    }

    #[allow(non_snake_case)]
    pub fn NonCliffordGate(msg: String) -> Self {
        Self(QuantumErrorEnum::NonCliffordGate(msg))
    }

    #[allow(non_snake_case)]
    pub fn CalculationError(msg: String) -> Self {
        Self(QuantumErrorEnum::CalculationError(msg))
    }
}

// Integration with the generic CausalityError, mirroring the physics crate.
impl From<QuantumError> for CausalityError {
    fn from(e: QuantumError) -> Self {
        CausalityError::new(CausalityErrorEnum::Custom(format!("{}", e)))
    }
}

impl From<deep_causality_metric::MetricError> for QuantumError {
    fn from(e: deep_causality_metric::MetricError) -> Self {
        QuantumError::new(QuantumErrorEnum::UnsupportedMetric(format!("{}", e)))
    }
}

impl core::error::Error for QuantumError {}

impl Display for QuantumError {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match &self.0 {
            QuantumErrorEnum::DimensionMismatch(msg) => write!(f, "Dimension Mismatch: {}", msg),
            QuantumErrorEnum::MetricMismatch(msg) => write!(f, "Metric Mismatch: {}", msg),
            QuantumErrorEnum::UnsupportedMetric(msg) => write!(f, "Unsupported Metric: {}", msg),
            QuantumErrorEnum::NonFiniteValue(msg) => write!(f, "Non-Finite Value: {}", msg),
            QuantumErrorEnum::NormalizationError(msg) => {
                write!(f, "Normalization Error: {}", msg)
            }
            QuantumErrorEnum::NonPositiveOperator(msg) => {
                write!(f, "Non-Positive Operator: {}", msg)
            }
            QuantumErrorEnum::NonUnitTrace(msg) => write!(f, "Non-Unit Trace: {}", msg),
            QuantumErrorEnum::NonCptpChannel(msg) => write!(f, "Non-CPTP Channel: {}", msg),
            QuantumErrorEnum::PartialTraceShape(msg) => {
                write!(f, "Partial Trace Shape Error: {}", msg)
            }
            QuantumErrorEnum::CommutatorNonZero {
                node_j,
                node_k,
                detail,
            } => write!(
                f,
                "Non-Zero Commutator: factors at nodes {} and {} do not commute: {}",
                node_j, node_k, detail
            ),
            QuantumErrorEnum::NotFaithfullyRepresentable(msg) => {
                write!(f, "Not Faithfully Representable (C3 obstruction): {}", msg)
            }
            QuantumErrorEnum::CertificateNotInherited {
                node_j,
                node_k,
                detail,
            } => write!(
                f,
                "Certificate Not Inherited: the parts' factors at nodes {} and {} do not certify the composite: {}",
                node_j, node_k, detail
            ),
            QuantumErrorEnum::CyclicStructureUnsupported(msg) => {
                write!(f, "Cyclic Structure Unsupported: {}", msg)
            }
            QuantumErrorEnum::HypothesisCountExceeded { n, pairs } => write!(
                f,
                "Hypothesis Count Exceeded: {} hypotheses give {} pairs, above the design cap",
                n, pairs
            ),
            QuantumErrorEnum::BoundaryNotHeld(msg) => write!(f, "Boundary Not Held: {}", msg),
            QuantumErrorEnum::NotInNormalizer { generator, detail } => write!(
                f,
                "Not In Normalizer: anticommutes with stabilizer generator {} ({})",
                generator, detail
            ),
            QuantumErrorEnum::NonCliffordGate(msg) => write!(f, "Non-Clifford Gate: {}", msg),
            QuantumErrorEnum::CalculationError(msg) => write!(f, "Calculation Error: {}", msg),
        }
    }
}