qualia_core_db/modalities/logic/
qubo.rs1use crate::NQuin;
7
8pub const OP_EMIT_WEIGHT: u8 = 0x50;
9pub const MAX_QUBO_VARS: usize = 64;
10pub const MAX_COUPLERS: usize = 512;
11
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum QuboCompileError {
14 VarBufferFull,
15 CouplerBufferFull,
16 IndexMapFull,
17 ClassifiedEgress,
18}
19
20#[repr(C)]
21#[derive(Debug, Clone, Copy)]
22pub struct QuboWeightEmit {
23 pub var_a: u8,
24 pub var_b: u8,
25 pub weight: f32,
26}
27
28#[derive(Debug, Clone)]
29pub struct QuboMatrix {
30 pub num_vars: u8,
31 pub linear: [f32; MAX_QUBO_VARS],
32 pub couplers: [QuboWeightEmit; MAX_COUPLERS],
33 pub coupler_count: usize,
34 pub index_map: [(u64, u8); MAX_QUBO_VARS],
35 pub index_count: usize,
36}
37
38impl Default for QuboMatrix {
39 fn default() -> Self {
40 Self {
41 num_vars: 0,
42 linear: [0.0; MAX_QUBO_VARS],
43 couplers: [QuboWeightEmit {
44 var_a: 0,
45 var_b: 0,
46 weight: 0.0,
47 }; MAX_COUPLERS],
48 coupler_count: 0,
49 index_map: [(0, 0); MAX_QUBO_VARS],
50 index_count: 0,
51 }
52 }
53}
54
55impl QuboMatrix {
56 fn map_var(&mut self, entity_hash: u64) -> Result<u8, QuboCompileError> {
57 for i in 0..self.index_count {
58 if self.index_map[i].0 == entity_hash {
59 return Ok(self.index_map[i].1);
60 }
61 }
62 if self.index_count >= MAX_QUBO_VARS {
63 return Err(QuboCompileError::IndexMapFull);
64 }
65 let idx = self.index_count as u8;
66 self.index_map[self.index_count] = (entity_hash, idx);
67 self.index_count += 1;
68 if idx as usize + 1 > self.num_vars as usize {
69 self.num_vars = idx + 1;
70 }
71 Ok(idx)
72 }
73
74 pub fn emit_linear(&mut self, var: u8, bias: f32) -> Result<(), QuboCompileError> {
75 if var as usize >= MAX_QUBO_VARS {
76 return Err(QuboCompileError::VarBufferFull);
77 }
78 self.linear[var as usize] += bias;
79 Ok(())
80 }
81
82 pub fn emit_coupler(&mut self, a: u8, b: u8, weight: f32) -> Result<(), QuboCompileError> {
83 if self.coupler_count >= MAX_COUPLERS {
84 return Err(QuboCompileError::CouplerBufferFull);
85 }
86 self.couplers[self.coupler_count] = QuboWeightEmit {
87 var_a: a,
88 var_b: b,
89 weight,
90 };
91 self.coupler_count += 1;
92 Ok(())
93 }
94
95 pub fn wipe_index_map(&mut self) {
96 for slot in &mut self.index_map {
97 unsafe {
98 std::ptr::write_volatile(&mut slot.0, 0);
99 }
100 }
101 self.index_count = 0;
102 }
103}
104
105pub fn compile_quins_to_qubo(
107 quins: &[NQuin],
108 out: &mut QuboMatrix,
109) -> Result<(), QuboCompileError> {
110 *out = QuboMatrix::default();
111 for q in quins {
112 if q.get_sensitivity_byte() == NQuin::SENSITIVITY_CLASSIFIED {
113 return Err(QuboCompileError::ClassifiedEgress);
114 }
115 let subj = out.map_var(q.subject)?;
116 let obj = out.map_var(q.object)?;
117 let pred_low = (q.predicate & 0xFF) as u8;
118 let weight = if pred_low == OP_EMIT_WEIGHT {
119 decode_inline_weight(q.object)
120 } else {
121 penalty_from_predicate(pred_low)
122 };
123 if subj == obj {
124 out.emit_linear(subj, weight)?;
125 } else {
126 out.emit_coupler(subj, obj, weight)?;
127 out.emit_linear(subj, weight * 0.5)?;
128 out.emit_linear(obj, weight * 0.5)?;
129 }
130 }
131 Ok(())
132}
133
134pub fn emit_weight_from_quin(
136 quin: &NQuin,
137 matrix: &mut QuboMatrix,
138) -> Result<(), QuboCompileError> {
139 let subj = matrix.map_var(quin.subject)?;
140 let obj = matrix.map_var(quin.object)?;
141 let w = decode_inline_weight(quin.object);
142 if subj == obj {
143 matrix.emit_linear(subj, w)
144 } else {
145 matrix.emit_coupler(subj, obj, w)
146 }
147}
148
149fn decode_inline_weight(object: u64) -> f32 {
150 let tag = (object >> 60) & 0x7;
151 if tag == 0b010 {
152 let scaled = (object & 0x0FFF_FFFF_FFFF_FFFF) as i64;
153 (scaled as f32) / 1_000_000.0
154 } else {
155 let raw = (object & 0xFFFF) as i32;
156 (raw as f32) / 100.0
157 }
158}
159
160fn penalty_from_predicate(opcode: u8) -> f32 {
161 match opcode {
162 0x10 => 5.0, 0x11 => -2.0, 0x12 => 8.0, _ => 1.0,
166 }
167}
168
169pub fn solve_classical(matrix: &QuboMatrix, assignment: &mut [u8; MAX_QUBO_VARS]) -> f32 {
171 let n = matrix.num_vars as usize;
172 for i in 0..n {
173 assignment[i] = 0;
174 }
175 let mut improved = true;
176 let mut energy = qubo_energy(matrix, assignment, n);
177 while improved {
178 improved = false;
179 for i in 0..n {
180 assignment[i] = 1 - assignment[i];
181 let new_e = qubo_energy(matrix, assignment, n);
182 if new_e < energy {
183 energy = new_e;
184 improved = true;
185 } else {
186 assignment[i] = 1 - assignment[i];
187 }
188 }
189 }
190 energy
191}
192
193fn qubo_energy(matrix: &QuboMatrix, assignment: &[u8; MAX_QUBO_VARS], n: usize) -> f32 {
194 let mut e = 0.0f32;
195 for i in 0..n {
196 if assignment[i] == 1 {
197 e += matrix.linear[i];
198 }
199 }
200 for c in 0..matrix.coupler_count {
201 let cw = matrix.couplers[c];
202 let a = cw.var_a as usize;
203 let b = cw.var_b as usize;
204 if a < n && b < n && assignment[a] == 1 && assignment[b] == 1 {
205 e += cw.weight;
206 }
207 }
208 e
209}
210
211pub fn rehydrate_solution(
213 matrix: &mut QuboMatrix,
214 assignment: &[u8; MAX_QUBO_VARS],
215 out: &mut [NQuin],
216) -> usize {
217 let mut count = 0;
218 for i in 0..matrix.index_count {
219 if count >= out.len() {
220 break;
221 }
222 let (entity, var) = matrix.index_map[i];
223 let val = assignment[var as usize];
224 let predicate = crate::q_hash("q42:quantumAssignment");
225 let context = crate::q_hash("q42:rehydrated");
226 let object = if val == 1 { 1 } else { 0 };
227 let q = NQuin {
228 subject: entity,
229 predicate,
230 object,
231 context,
232 metadata: 0xC000_0000_0000_0003,
233 parity: entity ^ predicate ^ object ^ context,
234 };
235 out[count] = q;
236 count += 1;
237 }
238 matrix.wipe_index_map();
239 count
240}
241
242pub fn quantum_prompt_gate(prompt: &str) -> Option<&'static str> {
244 let lower = prompt.to_lowercase();
245 if lower.contains("classified") && (lower.contains("[qpu:") || lower.contains("quantum")) {
246 return Some("FATAL: Cannot egress 0x02_CLASSIFIED assertions to a remote QPU.");
247 }
248 if lower.contains("sensitivitylabel") && lower.contains("0x02") {
249 return Some("FATAL: Classified sensitivity label blocks QPU egress.");
250 }
251 None
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn compile_empty_quins() {
260 let mut m = QuboMatrix::default();
261 assert!(compile_quins_to_qubo(&[], &mut m).is_ok());
262 assert_eq!(m.num_vars, 0);
263 }
264
265 #[test]
266 fn classified_quin_blocks_egress() {
267 let mut q = NQuin {
268 subject: 1,
269 predicate: 0,
270 object: 2,
271 context: 0,
272 metadata: 0,
273 parity: 0,
274 };
275 q.set_sensitivity_byte(NQuin::SENSITIVITY_CLASSIFIED);
276 let mut m = QuboMatrix::default();
277 assert_eq!(
278 compile_quins_to_qubo(&[q], &mut m),
279 Err(QuboCompileError::ClassifiedEgress)
280 );
281 }
282
283 #[test]
284 fn classical_solver_finds_low_energy() {
285 let mut m = QuboMatrix::default();
286 m.num_vars = 2;
287 m.linear[0] = -1.0;
288 m.linear[1] = -1.0;
289 m.couplers[0] = QuboWeightEmit {
290 var_a: 0,
291 var_b: 1,
292 weight: 2.0,
293 };
294 m.coupler_count = 1;
295 let mut assign = [0u8; MAX_QUBO_VARS];
296 let e = solve_classical(&m, &mut assign);
297 assert!(e <= 0.0);
298 }
299}