1use crate::error::PrivacyError;
17use crate::types::{FiveDimCoord, Hyperedge, HypergraphVertex, PrivacyProof, ProofScheme};
18use serde::{Deserialize, Serialize};
19use sha2::{Digest, Sha256};
20
21extern crate alloc;
22use alloc::{collections::BTreeMap, string::{String, ToString}, vec, vec::Vec};
23
24pub const TSIRELSON_BOUND: f64 = 2.828_427;
27
28pub const CHSH_THRESHOLD: f64 = 2.8;
30
31pub const LYAPUNOV_MIN: f64 = 4.5;
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct PrivacyHypergraph {
40 pub vertices: BTreeMap<String, HypergraphVertex>,
41 pub hyperedges: BTreeMap<String, Hyperedge>,
42 pub genesis: Option<String>,
44 pub kk_factor: f64,
46}
47
48impl PrivacyHypergraph {
49 pub fn new(chaos_seed: f64) -> Self {
53 let genesis_id = "genesis".to_string();
54 let genesis = HypergraphVertex {
55 id: genesis_id.clone(),
56 coord: FiveDimCoord::zero(),
57 commitment: hex::encode(Sha256::digest(b"5d-ezph-genesis")),
58 expiry_ms: 0,
59 };
60 let mut vertices = BTreeMap::new();
61 vertices.insert(genesis_id.clone(), genesis);
62
63 Self {
64 vertices,
65 hyperedges: BTreeMap::new(),
66 genesis: Some(genesis_id),
67 kk_factor: chaos_seed,
68 }
69 }
70
71 #[allow(clippy::too_many_arguments)] pub fn encode_vertex(
81 &mut self,
82 id: impl Into<String>,
83 spatial: f64,
84 temporal: f64,
85 dp_epsilon: f64,
86 phase_angle: f64,
87 chaos_traj: f64,
88 expiry_ms: u64,
89 ) -> Result<String, PrivacyError> {
90 let id = id.into();
91 let coord = FiveDimCoord {
92 spatial,
93 temporal,
94 probabilistic: dp_epsilon,
95 quantum: phase_angle,
96 chaotic: chaos_traj,
97 };
98
99 let mut hasher = Sha256::new();
101 hasher.update(id.as_bytes());
102 hasher.update(spatial.to_le_bytes());
103 hasher.update(temporal.to_le_bytes());
104 hasher.update(dp_epsilon.to_le_bytes());
105 hasher.update(phase_angle.to_le_bytes());
106 hasher.update(chaos_traj.to_le_bytes());
107 let commitment = hex::encode(hasher.finalize());
108
109 let vertex = HypergraphVertex { id: id.clone(), coord, commitment, expiry_ms };
110 self.vertices.insert(id.clone(), vertex);
111 Ok(id)
112 }
113
114 pub fn form_hyperedge(
119 &mut self,
120 id: impl Into<String>,
121 vertex_ids: Vec<String>,
122 chaos_perturbation: f64,
123 ) -> Result<String, PrivacyError> {
124 if vertex_ids.len() < 2 {
125 return Err(PrivacyError::InvalidDimension(vertex_ids.len()));
126 }
127
128 for vid in &vertex_ids {
130 if !self.vertices.contains_key(vid) {
131 return Err(PrivacyError::VertexNotFound(vid.clone()));
132 }
133 }
134
135 let k = vertex_ids.len() as f64;
137 let phase_product: f64 = vertex_ids
138 .iter()
139 .filter_map(|vid| self.vertices.get(vid))
140 .map(|v| v.coord.quantum)
141 .fold(1.0_f64, |acc, phi| acc * phi.cos());
142
143 let chsh = (2.0_f64.powf(k / 2.0 - 1.0))
145 * phase_product.abs()
146 * self.kk_factor
147 * (1.0 + chaos_perturbation.abs() * 0.1);
148
149 let chsh = chsh.min(TSIRELSON_BOUND);
151
152 let id = id.into();
153 let edge = Hyperedge {
154 id: id.clone(),
155 vertices: vertex_ids,
156 kk_metric: self.kk_factor,
157 chsh_value: chsh,
158 };
159 self.hyperedges.insert(id.clone(), edge);
160 Ok(id)
161 }
162
163 pub fn traverse_non_local(
168 &self,
169 start: &str,
170 max_hops: usize,
171 ) -> Result<Vec<String>, PrivacyError> {
172 let mut path = vec![start.to_string()];
173 let mut current = start.to_string();
174
175 for _ in 0..max_hops {
176 let next_edge = self
178 .hyperedges
179 .values()
180 .filter(|e| e.vertices.contains(¤t) && e.chsh_value > CHSH_THRESHOLD)
181 .max_by(|a, b| a.chsh_value.partial_cmp(&b.chsh_value).unwrap());
182
183 match next_edge {
184 Some(edge) => {
185 let next = edge
187 .vertices
188 .iter()
189 .filter(|v| **v != current)
190 .max_by(|a, b| {
191 let pa = self.vertices.get(*a).map(|v| v.coord.quantum).unwrap_or(0.0);
192 let pb = self.vertices.get(*b).map(|v| v.coord.quantum).unwrap_or(0.0);
193 pa.partial_cmp(&pb).unwrap()
194 });
195 match next {
196 Some(n) => {
197 current = n.clone();
198 path.push(current.clone());
199 }
200 None => break,
201 }
202 }
203 None => break,
204 }
205 }
206
207 Ok(path)
208 }
209
210 pub fn prove_non_locality(&self) -> Result<PrivacyProof, PrivacyError> {
215 let max_chsh = self
216 .hyperedges
217 .values()
218 .map(|e| e.chsh_value)
219 .fold(0.0_f64, f64::max);
220
221 if max_chsh <= CHSH_THRESHOLD {
222 return Err(PrivacyError::ChshBoundViolation {
223 got: max_chsh,
224 threshold: CHSH_THRESHOLD,
225 });
226 }
227
228 let mut hasher = Sha256::new();
230 hasher.update((self.vertices.len() as u64).to_le_bytes());
231 hasher.update((self.hyperedges.len() as u64).to_le_bytes());
232 hasher.update(max_chsh.to_le_bytes());
233 hasher.update(self.kk_factor.to_le_bytes());
234 let commitment = hex::encode(hasher.finalize());
235
236 Ok(PrivacyProof {
237 proof_bytes: commitment.clone(),
238 public_inputs: hex::encode(max_chsh.to_le_bytes()),
239 scheme: ProofScheme::Snark,
240 security_bits: 128,
241 proof_size: commitment.len(),
242 chsh_value: max_chsh,
243 lyapunov: LYAPUNOV_MIN,
244 })
245 }
246
247 pub fn prune_expired(&mut self, now_ms: u64) {
249 let expired: Vec<String> = self
250 .vertices
251 .iter()
252 .filter(|(_, v)| v.expiry_ms > 0 && now_ms > v.expiry_ms)
253 .map(|(id, _)| id.clone())
254 .collect();
255
256 for id in &expired {
257 self.vertices.remove(id);
258 self.hyperedges.retain(|_, e| !e.vertices.contains(id));
260 }
261 }
262
263 pub fn vertex_count(&self) -> usize {
265 self.vertices.len()
266 }
267
268 pub fn edge_count(&self) -> usize {
270 self.hyperedges.len()
271 }
272}
273
274pub fn hypergraph_chsh_value(graph_json: &str) -> Result<f64, PrivacyError> {
292 let graph: PrivacyHypergraph = serde_json::from_str(graph_json)
293 .map_err(|e| PrivacyError::SerializationError(e.to_string()))?;
294
295 let mut chsh_vals: Vec<f64> = graph
297 .hyperedges
298 .values()
299 .map(|e| e.chsh_value)
300 .collect();
301 chsh_vals.sort_by(|a, b| b.partial_cmp(a).unwrap_or(core::cmp::Ordering::Equal));
302
303 const BELL: f64 = core::f64::consts::FRAC_1_SQRT_2; while chsh_vals.len() < 4 {
308 chsh_vals.push(BELL);
309 }
310
311 let e_ab = chsh_vals[0];
314 let e_ab2 = chsh_vals[1];
315 let e_a2b = chsh_vals[2];
316 let e_a2b2 = chsh_vals[3];
317
318 let s = (e_ab + e_ab2 + e_a2b - e_a2b2).abs();
319 Ok(s)
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn test_genesis_vertex() {
328 let g = PrivacyHypergraph::new(1.0);
329 assert_eq!(g.vertex_count(), 1);
330 assert!(g.genesis.is_some());
331 }
332
333 #[test]
334 fn test_encode_vertex() {
335 let mut g = PrivacyHypergraph::new(1.0);
336 let id = g.encode_vertex("v1", 0.5, 1000.0, 1e-6, 1.2, 0.8, 0).unwrap();
337 assert_eq!(id, "v1");
338 assert_eq!(g.vertex_count(), 2);
339 }
340
341 #[test]
342 fn test_hyperedge_chsh() {
343 let mut g = PrivacyHypergraph::new(3.0);
344 g.encode_vertex("v1", 0.5, 1000.0, 1e-6, core::f64::consts::PI / 4.0, 0.8, 0).unwrap();
345 g.encode_vertex("v2", 0.7, 1001.0, 1e-6, core::f64::consts::PI / 3.0, 0.9, 0).unwrap();
346 let eid = g.form_hyperedge("e1", vec!["v1".into(), "v2".into()], 0.5).unwrap();
347 let edge = g.hyperedges.get(&eid).unwrap();
348 assert!(edge.chsh_value > 0.0);
350 }
351}