Skip to main content

qualia_core_db/
qpu_ingress.rs

1//! QPU response ingress firewall — collapses provider JSON into Quins / bitstrings.
2
3use crate::NQuin;
4
5pub const MAX_QPU_SAMPLES: usize = 64;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum QpuIngressError {
9    InvalidPayload,
10    SampleBufferFull,
11}
12
13/// D-Wave sample: binary assignment per variable.
14pub fn parse_dwave_samples(
15    json: &str,
16    out_bits: &mut [u8; MAX_QPU_SAMPLES],
17    out_len: &mut usize,
18) -> Result<(), QpuIngressError> {
19    let v: serde_json::Value =
20        serde_json::from_str(json).map_err(|_| QpuIngressError::InvalidPayload)?;
21    let samples = v
22        .pointer("/solutions/0/solutions")
23        .or_else(|| v.pointer("/samples"))
24        .and_then(|s| s.as_array())
25        .ok_or(QpuIngressError::InvalidPayload)?;
26
27    *out_len = 0;
28    if let Some(first) = samples.first() {
29        if let Some(sample_str) = first.get("sample").and_then(|s| s.as_array()) {
30            for bit in sample_str {
31                if *out_len >= MAX_QPU_SAMPLES {
32                    return Err(QpuIngressError::SampleBufferFull);
33                }
34                out_bits[*out_len] = bit.as_i64().unwrap_or(0) as u8;
35                *out_len += 1;
36            }
37            return Ok(());
38        }
39        if let Some(sample_obj) = first.as_object() {
40            let mut idx = 0usize;
41            while sample_obj.contains_key(&idx.to_string()) {
42                if idx >= MAX_QPU_SAMPLES {
43                    return Err(QpuIngressError::SampleBufferFull);
44                }
45                out_bits[idx] = sample_obj[&idx.to_string()].as_i64().unwrap_or(0) as u8;
46                idx += 1;
47            }
48            *out_len = idx;
49            return Ok(());
50        }
51    }
52    Err(QpuIngressError::InvalidPayload)
53}
54
55/// IBM job result: quasi-probability bitstrings or counts.
56pub fn parse_ibm_counts(
57    json: &str,
58    out_bits: &mut [u8; MAX_QPU_SAMPLES],
59    out_len: &mut usize,
60) -> Result<(), QpuIngressError> {
61    let v: serde_json::Value =
62        serde_json::from_str(json).map_err(|_| QpuIngressError::InvalidPayload)?;
63    let counts = v
64        .pointer("/results/0/data/counts")
65        .or_else(|| v.pointer("/counts"))
66        .and_then(|c| c.as_object())
67        .ok_or(QpuIngressError::InvalidPayload)?;
68
69    let (best_key, _) = counts
70        .iter()
71        .max_by_key(|(_, v)| v.as_i64().unwrap_or(0))
72        .ok_or(QpuIngressError::InvalidPayload)?;
73
74    *out_len = 0;
75    for ch in best_key.chars() {
76        if *out_len >= MAX_QPU_SAMPLES {
77            return Err(QpuIngressError::SampleBufferFull);
78        }
79        out_bits[*out_len] = if ch == '1' { 1 } else { 0 };
80        *out_len += 1;
81    }
82    Ok(())
83}
84
85/// Pack bitstring into provenance Quins for orchestrator post-flight.
86pub fn bits_to_provenance_quins(bits: &[u8], num_vars: u8, out: &mut [NQuin]) -> usize {
87    let mut n = 0usize;
88    for (i, &b) in bits.iter().enumerate().take(num_vars as usize) {
89        if n >= out.len() {
90            break;
91        }
92        let predicate = crate::q_hash("q42:qpuBit");
93        let context = crate::q_hash("q42:qpuIngress");
94        let subject = i as u64;
95        let object = b as u64;
96        let q = NQuin {
97            subject,
98            predicate,
99            object,
100            context,
101            metadata: 0xC000_0000_0000_0001,
102            parity: subject ^ predicate ^ object ^ context,
103        };
104        out[n] = q;
105        n += 1;
106    }
107    n
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn parse_ibm_counts_bitstring() {
116        let json = r#"{"counts":{"101":42,"010":1}}"#;
117        let mut bits = [0u8; MAX_QPU_SAMPLES];
118        let mut len = 0;
119        parse_ibm_counts(json, &mut bits, &mut len).unwrap();
120        assert_eq!(len, 3);
121        assert_eq!(bits[0], 1);
122        assert_eq!(bits[1], 0);
123        assert_eq!(bits[2], 1);
124    }
125}