Skip to main content

qec_code/
distance_exact.rs

1use serde::{Deserialize, Serialize};
2
3use crate::distance::{DistanceResult, LogicalClass};
4use crate::distance_bound::DistanceBoundWitness;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "kebab-case")]
8pub enum ExactCssDistanceMethod {
9    RstimIlpExact,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum ExactDistanceBoundType {
15    Exact,
16    Upper,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum ExactCssDistanceStatus {
22    Completed,
23    Timeout,
24    Incomplete,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
28#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
29#[serde(rename_all = "lowercase")]
30pub enum ExactCssDistanceBackend {
31    #[default]
32    Auto,
33    Highs,
34    Gurobi,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
38pub struct ExactCssDistanceSolverOptions {
39    #[serde(default)]
40    pub backend: ExactCssDistanceBackend,
41    pub time_limit_seconds: Option<f64>,
42    pub mip_gap: Option<f64>,
43    pub threads: Option<u32>,
44    #[serde(default, skip_serializing_if = "is_false")]
45    pub verbose_solver: bool,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum ExactCssDistanceSolverStatus {
51    Optimal,
52    TimeLimit,
53    SolutionLimit,
54    SubOptimal,
55}
56
57impl ExactCssDistanceSolverStatus {
58    pub fn is_exact(self) -> bool {
59        matches!(self, Self::Optimal)
60    }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64pub struct ExactCssDistanceSolverReport {
65    pub backend: ExactCssDistanceBackend,
66    pub status: ExactCssDistanceSolverStatus,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(tag = "input", rename_all = "snake_case")]
71pub enum ExactCssDistanceInput {
72    CodeId { code_id: String },
73    Files { hx: String, hz: String },
74    QuantumTannerSpec { quantum_tanner_spec: String },
75}
76
77#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
78pub struct ExactCssDistanceOptions {
79    #[serde(flatten)]
80    pub input: ExactCssDistanceInput,
81    #[serde(flatten)]
82    pub solver: ExactCssDistanceSolverOptions,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub struct ExactCssDistanceProvenance {
87    pub tool: String,
88    pub tool_version: String,
89    pub method_revision: u32,
90}
91
92impl ExactCssDistanceProvenance {
93    pub fn current() -> Self {
94        Self {
95            tool: "qec-code".to_owned(),
96            tool_version: env!("CARGO_PKG_VERSION").to_owned(),
97            method_revision: 1,
98        }
99    }
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct ExactCssDistanceResult {
104    pub status: ExactCssDistanceStatus,
105    pub distance: usize,
106    pub method: ExactCssDistanceMethod,
107    pub bound_type: ExactDistanceBoundType,
108    pub logical_class: LogicalClass,
109    pub witness: DistanceBoundWitness,
110    #[serde(default)]
111    pub requested_backend: ExactCssDistanceBackend,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub backend: Option<ExactCssDistanceBackend>,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub solver_status: Option<ExactCssDistanceSolverStatus>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub time_limit_seconds: Option<f64>,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub mip_gap: Option<f64>,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub threads: Option<u32>,
122    #[serde(default, skip_serializing_if = "is_false")]
123    pub verbose_solver: bool,
124    pub options: ExactCssDistanceOptions,
125    pub provenance: ExactCssDistanceProvenance,
126}
127
128impl ExactCssDistanceResult {
129    pub fn completed(distance: DistanceResult, options: ExactCssDistanceOptions) -> Self {
130        Self::completed_with_solver_report(distance, options, None)
131    }
132
133    pub fn completed_with_solver_report(
134        distance: DistanceResult,
135        options: ExactCssDistanceOptions,
136        solver_report: Option<ExactCssDistanceSolverReport>,
137    ) -> Self {
138        let solver_status = solver_report.map(|report| report.status);
139        let backend = solver_report.map(|report| report.backend);
140        let solver_certifies_exact = solver_status
141            .map(ExactCssDistanceSolverStatus::is_exact)
142            .unwrap_or(true);
143        let options_allow_exact = !has_positive_mip_gap(&options.solver);
144        let is_exact = solver_certifies_exact && options_allow_exact;
145        let status = match solver_status {
146            Some(ExactCssDistanceSolverStatus::TimeLimit) => ExactCssDistanceStatus::Timeout,
147            _ if !is_exact => ExactCssDistanceStatus::Incomplete,
148            _ => ExactCssDistanceStatus::Completed,
149        };
150        let bound_type = if is_exact {
151            ExactDistanceBoundType::Exact
152        } else {
153            ExactDistanceBoundType::Upper
154        };
155
156        Self {
157            status,
158            distance: distance.distance,
159            method: ExactCssDistanceMethod::RstimIlpExact,
160            bound_type,
161            logical_class: distance.logical_class,
162            witness: DistanceBoundWitness::from_pauli(&distance.witness),
163            requested_backend: options.solver.backend,
164            backend,
165            solver_status,
166            time_limit_seconds: options.solver.time_limit_seconds,
167            mip_gap: options.solver.mip_gap,
168            threads: options.solver.threads,
169            verbose_solver: options.solver.verbose_solver,
170            options,
171            provenance: ExactCssDistanceProvenance::current(),
172        }
173    }
174}
175
176fn is_false(value: &bool) -> bool {
177    !*value
178}
179
180fn has_positive_mip_gap(options: &ExactCssDistanceSolverOptions) -> bool {
181    options.mip_gap.map(|gap| gap > 0.0).unwrap_or(false)
182}