Skip to main content

qualia_core_db/solvers/qpu/
mod.rs

1//! QPU (Quantum Processing Unit) solver integration.
2//!
3//! Moved from the standalone `qpu/` crate.  Config-file loading and the old
4//! single-endpoint `QpuClient` have been removed; authentication and HTTP
5//! egress are now handled by `qualia-client-core::qpu_oracle` and
6//! `qualia-client-core::qpu_dispatcher`.
7
8pub mod dispatcher;
9pub mod pre_solver;
10
11use serde::{Deserialize, Serialize};
12
13// ── Problem type ──────────────────────────────────────────────────────────────
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub enum ProblemType {
17    /// Quantum annealing (QUBO)
18    Annealing,
19    /// Gate-model quantum circuit
20    GateModel,
21    /// Variational Quantum Eigensolver
22    Vqe,
23    /// Quantum Approximate Optimisation Algorithm
24    Qaoa,
25}
26
27// ── Job types ─────────────────────────────────────────────────────────────────
28
29/// Parameters for a QPU job submission.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct JobParameters {
32    pub num_qubits: u32,
33    /// Hamiltonian JSON (annealing problems)
34    pub hamiltonian: Option<String>,
35    /// Circuit JSON (gate-model problems)
36    pub circuit: Option<String>,
37    pub circuit_depth: u32,
38    pub shots: u32,
39    pub extra: serde_json::Value,
40}
41
42impl Default for JobParameters {
43    fn default() -> Self {
44        Self {
45            num_qubits: 1,
46            hamiltonian: None,
47            circuit: None,
48            circuit_depth: 1,
49            shots: 1000,
50            extra: serde_json::Value::Null,
51        }
52    }
53}
54
55/// A QPU job ready for dispatch.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct QpuJob {
58    pub job_id: String,
59    pub problem_type: ProblemType,
60    pub parameters: JobParameters,
61    /// Unix timestamp ms (wall-clock submission time)
62    pub created_at_ms: u64,
63}
64
65impl QpuJob {
66    pub fn new(job_id: String, problem_type: ProblemType, parameters: JobParameters) -> Self {
67        Self {
68            job_id,
69            problem_type,
70            parameters,
71            created_at_ms: current_time_ms(),
72        }
73    }
74}
75
76impl Default for QpuJob {
77    fn default() -> Self {
78        Self {
79            job_id: "default_job".into(),
80            problem_type: ProblemType::Annealing,
81            parameters: JobParameters::default(),
82            created_at_ms: 0,
83        }
84    }
85}
86
87// ── Result types ──────────────────────────────────────────────────────────────
88
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90pub enum JobStatus {
91    Pending,
92    Running,
93    Completed,
94    Failed,
95    Timeout,
96}
97
98/// Measurement result from a QPU run.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct Measurement {
101    pub bitstring: String,
102    pub count: u32,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct JobResultData {
107    pub measurements: Vec<Measurement>,
108    pub energies: Option<Vec<f64>>,
109    pub metadata: serde_json::Value,
110}
111
112/// Completed or failed QPU job result.
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct QpuResult {
115    pub job_id: String,
116    pub status: JobStatus,
117    pub result: Option<JobResultData>,
118    pub completed_at_ms: Option<u64>,
119    pub error: Option<String>,
120}
121
122impl QpuResult {
123    pub fn failed(job_id: String, msg: String) -> Self {
124        Self {
125            job_id,
126            status: JobStatus::Failed,
127            result: None,
128            completed_at_ms: Some(current_time_ms()),
129            error: Some(msg),
130        }
131    }
132}
133
134// ── Error ─────────────────────────────────────────────────────────────────────
135
136#[derive(Debug)]
137pub enum QpuError {
138    Api(String),
139    Network(String),
140    JobFailed(String),
141    Timeout,
142    NotUnlocked,
143}
144
145impl std::fmt::Display for QpuError {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        match self {
148            Self::Api(s) => write!(f, "QPU API error: {}", s),
149            Self::Network(s) => write!(f, "QPU network error: {}", s),
150            Self::JobFailed(s) => write!(f, "QPU job failed: {}", s),
151            Self::Timeout => write!(f, "QPU job timed out"),
152            Self::NotUnlocked => {
153                write!(f, "QPU Oracle not unlocked — affirm commitment in Settings")
154            }
155        }
156    }
157}
158
159impl std::error::Error for QpuError {}
160
161// ── Utilities ─────────────────────────────────────────────────────────────────
162
163fn current_time_ms() -> u64 {
164    std::time::SystemTime::now()
165        .duration_since(std::time::UNIX_EPOCH)
166        .map(|d| d.as_millis() as u64)
167        .unwrap_or(0)
168}