1use crate::qpu_dispatcher::{self, QpuDispatchResult};
4use crate::qpu_oracle::{self, QpuChatCommandResult};
5use qualia_core_db::qubo_compiler::{compile_quins_to_qubo, rehydrate_solution, QuboMatrix};
6use qualia_core_db::NQuin;
7
8pub const MAX_REHYDRATED: usize = 64;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum QuantumTaskKind {
12 QuboRouting,
13 DftGroundState,
14 DefeasibleResolution,
15}
16
17pub struct QuantumPipelineResult {
18 pub task: QuantumTaskKind,
19 pub dispatch: QpuDispatchResult,
20 pub rehydrated: Vec<NQuin>,
21 pub summary: String,
22}
23
24pub fn detect_task_from_prompt(prompt: &str) -> Option<QuantumTaskKind> {
25 let lower = prompt.to_lowercase();
26 if lower.contains("[qpu:qubo]") {
27 return Some(QuantumTaskKind::QuboRouting);
28 }
29 if lower.contains("[qpu:dft]") {
30 return Some(QuantumTaskKind::DftGroundState);
31 }
32 if lower.contains("[qpu:defeasible]") {
33 return Some(QuantumTaskKind::DefeasibleResolution);
34 }
35 if prompt.contains(r"$$") {
36 if prompt.contains(r"\min") || prompt.contains("QUBO") {
37 return Some(QuantumTaskKind::QuboRouting);
38 }
39 if prompt.contains(r"\hat{H}")
40 || prompt.contains(r"\Psi")
41 || prompt.contains(r"\hat{h}")
42 || prompt.contains("ground state")
43 {
44 return Some(QuantumTaskKind::DftGroundState);
45 }
46 }
47 None
48}
49
50pub fn execute_quantum_pipeline(
51 task: QuantumTaskKind,
52 quins: &[NQuin],
53 latex_hint: Option<&str>,
54) -> Result<QuantumPipelineResult, String> {
55 if !qpu_oracle::is_qpu_feature_unlocked() {
56 return Err("QPU Oracle not unlocked. Type [enable_QPU] in Chat first.".into());
57 }
58
59 let settings = qpu_oracle::get_qpu_settings();
60 let shots = settings.max_shots_per_task;
61
62 match task {
63 QuantumTaskKind::QuboRouting => {
64 let mut matrix = QuboMatrix::default();
65 compile_quins_to_qubo(quins, &mut matrix)
66 .map_err(|e| format!("QUBO compile blocked: {e:?}"))?;
67 if matrix.num_vars == 0 {
68 build_demo_qubo(&mut matrix, latex_hint);
69 }
70 let dispatch = qpu_dispatcher::dispatch_qubo(&matrix, shots)?;
71 let mut out = [NQuin {
72 subject: 0,
73 predicate: 0,
74 object: 0,
75 context: 0,
76 metadata: 0,
77 parity: 0,
78 }; MAX_REHYDRATED];
79 let n = rehydrate_solution(&mut matrix, &dispatch.assignment, &mut out);
80 let summary = format!(
81 "⚛️ **QUBO routing complete** ({})\n\n\
82 - Variables: {}\n\
83 - Ground energy: {:.4}\n\
84 - Remote QPU: {}\n\
85 - Re-hydrated assertions: {}\n\n\
86 Ephemeral index map wiped. No semantic context left the device.",
87 dispatch.backend, dispatch.num_vars, dispatch.energy, dispatch.used_remote, n
88 );
89 Ok(QuantumPipelineResult {
90 task,
91 dispatch,
92 rehydrated: out[..n].to_vec(),
93 summary,
94 })
95 }
96 QuantumTaskKind::DftGroundState => {
97 let params = extract_vqe_params(latex_hint.unwrap_or(""));
98 let dispatch = qpu_dispatcher::dispatch_vqe(¶ms, shots)?;
99 let summary = format!(
100 "⚛️ **DFT / VQE ground-state** ({})\n\n\
101 - Parameter vector dim: {}\n\
102 - Estimated energy: {:.4} eV\n\
103 - Remote QPU: {}\n\n\
104 Local Core 2 prepared the Hamiltonian; only parameter amplitudes egressed.",
105 dispatch.backend,
106 params.len(),
107 dispatch.energy,
108 dispatch.used_remote
109 );
110 Ok(QuantumPipelineResult {
111 task,
112 dispatch,
113 rehydrated: vec![],
114 summary,
115 })
116 }
117 QuantumTaskKind::DefeasibleResolution => {
118 let mut matrix = QuboMatrix::default();
119 compile_quins_to_qubo(quins, &mut matrix)
120 .map_err(|e| format!("Defeasible QUBO blocked: {e:?}"))?;
121 if matrix.num_vars == 0 {
122 build_defeasible_demo(&mut matrix);
123 }
124 let dispatch = qpu_dispatcher::dispatch_qubo(&matrix, shots)?;
125 let summary = format!(
126 "⚛️ **Defeasible resolution** via probabilistic QUBO ({})\n\
127 Energy: {:.4} | Remote: {}",
128 dispatch.backend, dispatch.energy, dispatch.used_remote
129 );
130 Ok(QuantumPipelineResult {
131 task,
132 dispatch,
133 rehydrated: vec![],
134 summary,
135 })
136 }
137 }
138}
139
140fn build_demo_qubo(matrix: &mut QuboMatrix, hint: Option<&str>) {
141 let _ = hint;
142 matrix.num_vars = 3;
143 matrix.linear[0] = -1.0;
144 matrix.linear[1] = -0.5;
145 matrix.linear[2] = -2.0;
146 let _ = matrix.emit_coupler(0, 1, 1.5);
147 let _ = matrix.emit_coupler(1, 2, 0.8);
148 matrix.index_map[0] = (0xA001, 0);
149 matrix.index_map[1] = (0xA002, 1);
150 matrix.index_map[2] = (0xA003, 2);
151 matrix.index_count = 3;
152}
153
154fn build_defeasible_demo(matrix: &mut QuboMatrix) {
155 matrix.num_vars = 2;
156 matrix.linear[0] = -1.0;
157 matrix.linear[1] = -1.0;
158 let _ = matrix.emit_coupler(0, 1, 3.0);
159 matrix.index_count = 2;
160 matrix.index_map[0] = (0xD001, 0);
161 matrix.index_map[1] = (0xD002, 1);
162}
163
164pub fn handle_engine_chat_command(text: &str) -> QpuChatCommandResult {
166 use crate::qpu_oracle::{handle_qpu_chat_command, QpuChatCommandResult};
167
168 let unlock = handle_qpu_chat_command(text);
169 if unlock.handled {
170 return unlock;
171 }
172
173 let task = detect_task_from_prompt(text);
174 if let Some(kind) = task {
175 if !qpu_oracle::is_qpu_feature_unlocked() {
176 return QpuChatCommandResult {
177 handled: true,
178 feature_unlocked: false,
179 response: "⚛️ Quantum task detected but QPU Oracle is locked. \
180 Type `[enable_QPU]` first, then configure API keys in Settings."
181 .to_string(),
182 };
183 }
184 let quins: Vec<NQuin> = vec![];
185 let latex = if text.contains(r"$$") {
186 Some(text)
187 } else {
188 None
189 };
190 match execute_quantum_pipeline(kind, &quins, latex) {
191 Ok(result) => QpuChatCommandResult {
192 handled: true,
193 feature_unlocked: true,
194 response: result.summary,
195 },
196 Err(e) => QpuChatCommandResult {
197 handled: true,
198 feature_unlocked: true,
199 response: format!("🔴 Quantum pipeline failed: {e}"),
200 },
201 }
202 } else {
203 QpuChatCommandResult {
204 handled: false,
205 response: String::new(),
206 feature_unlocked: qpu_oracle::is_qpu_feature_unlocked(),
207 }
208 }
209}
210
211fn extract_vqe_params(latex: &str) -> Vec<f64> {
212 let mut params = Vec::new();
213 for token in latex.split(|c: char| !c.is_ascii_digit() && c != '.' && c != '-') {
214 if let Ok(v) = token.parse::<f64>() {
215 if token.contains('.') || v.abs() > 0.0 {
216 params.push(v);
217 }
218 }
219 }
220 if params.is_empty() {
221 params.extend_from_slice(&[0.1, 0.2, -0.15, 0.05]);
222 }
223 params.truncate(16);
224 params
225}