deep_causality_quantum 0.3.0

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),
    /// An abstraction constructor was handed a process operator without its
    /// circuit. A bare `ProcessFactors` store is the marginal of a compositional
    /// model and not one itself (Lorenz & Tull, arXiv:2602.16612, Example 62),
    /// so it validates as in v1 and cannot enter an abstraction. Carries the subject's origin.
    NoCompositionalModel { origin: String },
    /// The numeric semantics would form a composite Choi operator of
    /// `2^(2n + 2k)` entries above its cap: `n` and `k` are the input and output
    /// qubit counts (rounded up from the dimensions), `entries` the count it
    /// would allocate, `cap` the limit. Refused before allocating.
    NaturalityDimensionExceeded {
        n: usize,
        k: usize,
        entries: u64,
        cap: u64,
    },
    /// The numeric semantics would carry a Kraus family (the product of the
    /// operator counts of every noise box and measurement outcome) above its cap.
    /// Refused before allocating.
    KrausFamilyExceeded { operators: u64, cap: u64 },
    /// A fault was asked to propagate through a program with no normal form of
    /// polynomial size: two non-Clifford layers separated by a non-diagonal
    /// Clifford. `layer` is the non-diagonal Clifford layer and `after` the
    /// non-Clifford layer whose remainder precedes it. The propagator refuses
    /// rather than expanding.
    NoPropagationNormalForm { layer: usize, after: usize },
    /// An interchange query named sets that are not parallelisable: the directed
    /// path `from → to` joins two members of interchange set `set` (Lorenz &
    /// Tull §7.2).
    NotParallelisable { set: usize, from: usize, to: usize },
    /// A type alignment's section does not invert its channel: for alignment
    /// entry `entry`, `‖τ_X ∘ E_X − id‖_F` exceeds the state tolerance; `detail`
    /// states the residual and the tolerance.
    SectionNotInverse { entry: usize, detail: 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 NoCompositionalModel(origin: String) -> Self {
        Self(QuantumErrorEnum::NoCompositionalModel { origin })
    }

    #[allow(non_snake_case)]
    pub fn NaturalityDimensionExceeded(n: usize, k: usize, entries: u64, cap: u64) -> Self {
        Self(QuantumErrorEnum::NaturalityDimensionExceeded { n, k, entries, cap })
    }

    #[allow(non_snake_case)]
    pub fn KrausFamilyExceeded(operators: u64, cap: u64) -> Self {
        Self(QuantumErrorEnum::KrausFamilyExceeded { operators, cap })
    }

    #[allow(non_snake_case)]
    pub fn NoPropagationNormalForm(layer: usize, after: usize) -> Self {
        Self(QuantumErrorEnum::NoPropagationNormalForm { layer, after })
    }

    #[allow(non_snake_case)]
    pub fn NotParallelisable(set: usize, from: usize, to: usize) -> Self {
        Self(QuantumErrorEnum::NotParallelisable { set, from, to })
    }

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

    #[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::NoCompositionalModel { origin } => write!(
                f,
                "No Compositional Model: a {} subject is the marginal of a compositional model and not one itself; only a circuit subject, whose dilation carries the model, can enter an abstraction",
                origin
            ),
            QuantumErrorEnum::NaturalityDimensionExceeded { n, k, entries, cap } => write!(
                f,
                "Naturality Dimension Exceeded: a channel from {} to {} qubits has a composite Choi of {} entries, above the cap of {}",
                n, k, entries, cap
            ),
            QuantumErrorEnum::KrausFamilyExceeded { operators, cap } => write!(
                f,
                "Kraus Family Exceeded: {} operators, above the cap of {}",
                operators, cap
            ),
            QuantumErrorEnum::NoPropagationNormalForm { layer, after } => write!(
                f,
                "No Propagation Normal Form: layer {} is a non-diagonal Clifford layer following the non-Clifford remainder left by layer {}; the propagated error is neither a Pauli nor diagonal and has no normal form of polynomial size",
                layer, after
            ),
            QuantumErrorEnum::NotParallelisable { set, from, to } => write!(
                f,
                "Not Parallelisable: interchange set {} holds nodes {} and {}, joined by the directed path {}{}",
                set, from, to, from, to
            ),
            QuantumErrorEnum::SectionNotInverse { entry, detail } => {
                write!(f, "Section Not Inverse: entry {}: {}", entry, detail)
            }
            QuantumErrorEnum::CalculationError(msg) => write!(f, "Calculation Error: {}", msg),
        }
    }
}