1use crate::NQuin;
25use std::collections::HashMap;
26
27#[derive(Debug, Clone)]
34pub struct TensorState {
35 pub data: Vec<f64>,
37 pub shape: Vec<usize>,
39 pub provenance: TensorProvenance,
41 pub state_id: u64,
43}
44
45impl TensorState {
46 pub fn new(data: Vec<f64>, shape: Vec<usize>) -> Self {
48 let state_id = Self::generate_state_id(&data, &shape);
49 Self {
50 data,
51 shape,
52 provenance: TensorProvenance::Root {
53 source: "initial_state".to_string(),
54 timestamp: Self::current_timestamp(),
55 },
56 state_id,
57 }
58 }
59
60 pub fn from_scalar(value: f64) -> Self {
62 Self::new(vec![value], vec![1])
63 }
64
65 pub fn apply_operation(&self, operation: &str, params: &HashMap<String, f64>) -> Self {
67 let new_data = self.compute_operation(operation, params);
68 let new_shape = self.infer_shape(operation, &new_data);
69 let state_id = Self::generate_state_id(&new_data, &new_shape);
70
71 Self {
72 data: new_data,
73 shape: new_shape,
74 provenance: TensorProvenance::Derived {
75 parent_id: self.state_id,
76 operation: operation.to_string(),
77 params: params.clone(),
78 timestamp: Self::current_timestamp(),
79 },
80 state_id,
81 }
82 }
83
84 fn compute_operation(&self, operation: &str, params: &HashMap<String, f64>) -> Vec<f64> {
86 match operation {
87 "rk4_step" => self.rk4_step(params),
88 "scale" => self.scale(params),
89 "add" => self.add(params),
90 "multiply" => self.multiply(params),
91 "transpose" => self.transpose(),
92 "reduce_sum" => self.reduce_sum(),
93 _ => self.data.clone(), }
95 }
96
97 fn rk4_step(&self, params: &HashMap<String, f64>) -> Vec<f64> {
99 let step_size = params.get("step_size").copied().unwrap_or(0.01);
100 let lambda = params.get("lambda").copied().unwrap_or(0.5);
101
102 self.data
104 .iter()
105 .map(|&y| y * (-lambda * step_size).exp())
106 .collect()
107 }
108
109 fn scale(&self, params: &HashMap<String, f64>) -> Vec<f64> {
111 let factor = params.get("factor").copied().unwrap_or(1.0);
112 self.data.iter().map(|&x| x * factor).collect()
113 }
114
115 fn add(&self, params: &HashMap<String, f64>) -> Vec<f64> {
117 let value = params.get("value").copied().unwrap_or(0.0);
118 self.data.iter().map(|&x| x + value).collect()
119 }
120
121 fn multiply(&self, params: &HashMap<String, f64>) -> Vec<f64> {
123 let value = params.get("value").copied().unwrap_or(1.0);
124 self.data.iter().map(|&x| x * value).collect()
125 }
126
127 fn transpose(&self) -> Vec<f64> {
129 if self.shape.len() == 2 {
130 let rows = self.shape[0];
131 let cols = self.shape[1];
132 let mut transposed = vec![0.0; self.data.len()];
133
134 for i in 0..rows {
135 for j in 0..cols {
136 transposed[j * rows + i] = self.data[i * cols + j];
137 }
138 }
139 transposed
140 } else {
141 self.data.clone()
142 }
143 }
144
145 fn reduce_sum(&self) -> Vec<f64> {
147 vec![self.data.iter().sum()]
148 }
149
150 fn infer_shape(&self, operation: &str, _data: &Vec<f64>) -> Vec<usize> {
152 match operation {
153 "reduce_sum" => vec![1],
154 "transpose" => {
155 if self.shape.len() == 2 {
156 vec![self.shape[1], self.shape[0]]
157 } else {
158 self.shape.clone()
159 }
160 }
161 _ => self.shape.clone(),
162 }
163 }
164
165 pub fn get_provenance_chain(&self) -> Vec<TensorProvenance> {
167 let chain = vec![self.provenance.clone()];
168 chain
170 }
171
172 pub fn to_quin(&self) -> NQuin {
174 let mut quin = NQuin::default();
175 quin.subject = self.state_id;
176
177 if !self.data.is_empty() {
180 quin.object = self.data[0].to_bits() as u64;
181 }
182
183 quin.metadata = self.data.len() as u64;
185
186 quin.context = self.provenance_hash();
188
189 quin
190 }
191
192 fn provenance_hash(&self) -> u64 {
194 match &self.provenance {
195 TensorProvenance::Root { source, timestamp } => {
196 let combined = format!("{}:{}", source, timestamp);
197 crate::q_hash(&combined)
198 }
199 TensorProvenance::Derived {
200 parent_id,
201 operation,
202 params,
203 timestamp,
204 } => {
205 let param_str: String = params
206 .iter()
207 .map(|(k, v)| format!("{}={}", k, v))
208 .collect::<Vec<_>>()
209 .join(",");
210 let combined = format!("{}:{}:{}:{}", parent_id, operation, param_str, timestamp);
211 crate::q_hash(&combined)
212 }
213 }
214 }
215
216 fn generate_state_id(data: &Vec<f64>, shape: &Vec<usize>) -> u64 {
218 let data_hash: u64 = data
219 .iter()
220 .map(|&x| x.to_bits())
221 .fold(0u64, |acc, x| acc.wrapping_add(x));
222
223 let shape_hash: u64 = shape
224 .iter()
225 .fold(0u64, |acc, &x| acc.wrapping_add(x as u64));
226
227 data_hash.wrapping_mul(31).wrapping_add(shape_hash)
228 }
229
230 fn current_timestamp() -> u64 {
232 std::time::SystemTime::now()
233 .duration_since(std::time::UNIX_EPOCH)
234 .unwrap()
235 .as_secs()
236 }
237
238 pub fn get_provenance(&self) -> &TensorProvenance {
240 &self.provenance
241 }
242}
243
244#[derive(Debug, Clone)]
248pub enum TensorProvenance {
249 Root { source: String, timestamp: u64 },
251 Derived {
253 parent_id: u64,
254 operation: String,
255 params: HashMap<String, f64>,
256 timestamp: u64,
257 },
258}
259
260impl TensorProvenance {
261 pub fn is_root(&self) -> bool {
263 matches!(self, TensorProvenance::Root { .. })
264 }
265
266 pub fn parent_id(&self) -> Option<u64> {
268 match self {
269 TensorProvenance::Derived { parent_id, .. } => Some(*parent_id),
270 _ => None,
271 }
272 }
273
274 pub fn operation(&self) -> Option<&str> {
276 match self {
277 TensorProvenance::Derived { operation, .. } => Some(operation),
278 _ => None,
279 }
280 }
281}
282
283pub struct ProvenanceGraph {
287 states: HashMap<u64, TensorState>,
289 edges: Vec<(u64, u64)>, }
292
293impl ProvenanceGraph {
294 pub fn new() -> Self {
296 Self {
297 states: HashMap::new(),
298 edges: Vec::new(),
299 }
300 }
301
302 pub fn add_state(&mut self, state: TensorState) {
304 let state_id = state.state_id;
305
306 if let Some(parent_id) = state.provenance.parent_id() {
308 self.edges.push((parent_id, state_id));
309 }
310
311 self.states.insert(state_id, state);
312 }
313
314 pub fn get_state(&self, state_id: u64) -> Option<&TensorState> {
316 self.states.get(&state_id)
317 }
318
319 pub fn get_lineage(&self, state_id: u64) -> Vec<u64> {
321 let mut lineage = Vec::new();
322 let mut current_id = state_id;
323
324 while let Some(state) = self.states.get(¤t_id) {
325 lineage.push(current_id);
326
327 if let Some(parent_id) = state.provenance.parent_id() {
328 current_id = parent_id;
329 } else {
330 break;
331 }
332 }
333
334 lineage
335 }
336
337 pub fn get_children(&self, state_id: u64) -> Vec<u64> {
339 self.edges
340 .iter()
341 .filter(|(parent, _)| *parent == state_id)
342 .map(|(_, child)| *child)
343 .collect()
344 }
345
346 pub fn validate(&self) -> Result<(), String> {
348 for (parent_id, child_id) in &self.edges {
350 if !self.states.contains_key(parent_id) {
351 return Err(format!("Parent state {} not found in graph", parent_id));
352 }
353 if !self.states.contains_key(child_id) {
354 return Err(format!("Child state {} not found in graph", child_id));
355 }
356 }
357
358 for state_id in self.states.keys() {
360 let lineage = self.get_lineage(*state_id);
361 if lineage.len()
362 != lineage
363 .iter()
364 .collect::<std::collections::HashSet<_>>()
365 .len()
366 {
367 return Err(format!("Cycle detected in lineage of state {}", state_id));
368 }
369 }
370
371 Ok(())
372 }
373}
374
375impl Default for ProvenanceGraph {
376 fn default() -> Self {
377 Self::new()
378 }
379}
380
381#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
388 fn test_tensor_state_creation() {
389 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
390 assert_eq!(state.data.len(), 3);
391 assert!(state.provenance.is_root());
392 }
393
394 #[test]
395 fn test_tensor_state_from_scalar() {
396 let state = TensorState::from_scalar(5.0);
397 assert_eq!(state.data, vec![5.0]);
398 assert_eq!(state.shape, vec![1]);
399 }
400
401 #[test]
402 fn test_apply_operation_rk4_step() {
403 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
404 let mut params = HashMap::new();
405 params.insert("step_size".to_string(), 0.01);
406 params.insert("lambda".to_string(), 0.5);
407
408 let transformed = state.apply_operation("rk4_step", ¶ms);
409 assert!(!transformed.provenance.is_root());
410 assert_eq!(transformed.provenance.operation(), Some("rk4_step"));
411 }
412
413 #[test]
414 fn test_apply_operation_scale() {
415 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
416 let mut params = HashMap::new();
417 params.insert("factor".to_string(), 2.0);
418
419 let transformed = state.apply_operation("scale", ¶ms);
420 assert_eq!(transformed.data, vec![2.0, 4.0, 6.0]);
421 }
422
423 #[test]
424 fn test_apply_operation_add() {
425 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
426 let mut params = HashMap::new();
427 params.insert("value".to_string(), 10.0);
428
429 let transformed = state.apply_operation("add", ¶ms);
430 assert_eq!(transformed.data, vec![11.0, 12.0, 13.0]);
431 }
432
433 #[test]
434 fn test_provenance_chain() {
435 let state1 = TensorState::new(vec![1.0], vec![1]);
436 let mut params = HashMap::new();
437 params.insert("factor".to_string(), 2.0);
438 let state2 = state1.apply_operation("scale", ¶ms);
439
440 let chain = state2.get_provenance_chain();
441 assert_eq!(chain.len(), 1);
444 assert!(!chain[0].is_root());
445 assert_eq!(chain[0].operation(), Some("scale"));
446 }
447
448 #[test]
449 fn test_provenance_graph() {
450 let mut graph = ProvenanceGraph::new();
451
452 let state1 = TensorState::new(vec![1.0], vec![1]);
453 let state1_id = state1.state_id;
454 graph.add_state(state1.clone());
455
456 let mut params = HashMap::new();
457 params.insert("factor".to_string(), 2.0);
458 let state2 = TensorState::from_scalar(1.0).apply_operation("scale", ¶ms);
459 let state2_id = state2.state_id;
460 graph.add_state(state2);
461
462 assert!(graph.get_state(state1_id).is_some());
463 assert!(graph.get_state(state2_id).is_some());
464 }
465
466 #[test]
467 fn test_provenance_graph_lineage() {
468 let mut graph = ProvenanceGraph::new();
469
470 let state1 = TensorState::new(vec![1.0], vec![1]);
471 let state1_id = state1.state_id;
472 graph.add_state(state1.clone());
473
474 let mut params = HashMap::new();
475 params.insert("factor".to_string(), 2.0);
476 let state2 = state1.apply_operation("scale", ¶ms);
477 let state2_id = state2.state_id;
478 graph.add_state(state2);
479
480 let lineage = graph.get_lineage(state2_id);
481 assert_eq!(lineage.len(), 2);
482 assert!(lineage.contains(&state1_id));
483 assert!(lineage.contains(&state2_id));
484 }
485
486 #[test]
487 fn test_provenance_graph_validate() {
488 let mut graph = ProvenanceGraph::new();
489
490 let state1 = TensorState::new(vec![1.0], vec![1]);
491 graph.add_state(state1.clone());
492
493 let mut params = HashMap::new();
494 params.insert("factor".to_string(), 2.0);
495 let state2 = state1.apply_operation("scale", ¶ms);
496 graph.add_state(state2);
497
498 assert!(graph.validate().is_ok());
499 }
500
501 #[test]
502 fn test_tensor_to_quin() {
503 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
504 let quin = state.to_quin();
505
506 assert_eq!(quin.subject, state.state_id);
507 assert_eq!(quin.metadata, 3);
508 }
509
510 #[test]
511 fn test_reduce_sum() {
512 let state = TensorState::new(vec![1.0, 2.0, 3.0], vec![3]);
513 let reduced = state.apply_operation("reduce_sum", &HashMap::new());
514
515 assert_eq!(reduced.data, vec![6.0]);
516 assert_eq!(reduced.shape, vec![1]);
517 }
518
519 #[test]
520 fn test_transpose_2d() {
521 let state = TensorState::new(vec![1.0, 2.0, 3.0, 4.0], vec![2, 2]);
522 let transposed = state.apply_operation("transpose", &HashMap::new());
523
524 assert_eq!(transposed.data, vec![1.0, 3.0, 2.0, 4.0]);
525 assert_eq!(transposed.shape, vec![2, 2]);
526 }
527}