scirs2_ndimage/advanced_fusion_algorithms/
temporal_causality.rs1use scirs2_core::ndarray::{Array2, Array3, ArrayView2};
26use scirs2_core::numeric::{Float, FromPrimitive};
27use std::cmp::Ordering;
28use std::collections::{BTreeMap, VecDeque};
29
30use super::config::*;
31use crate::error::NdimageResult;
32
33#[allow(dead_code)]
53pub fn analyze_temporal_causality<T>(
54 image: &ArrayView2<T>,
55 advancedstate: &mut AdvancedState,
56 config: &AdvancedConfig,
57) -> NdimageResult<Array2<f64>>
58where
59 T: Float + FromPrimitive + Copy,
60{
61 let (height, width) = image.dim();
62 let mut causal_output = Array2::zeros((height, width));
63
64 let current_temporal = image_to_temporal_representation(image)?;
66
67 advancedstate
69 .temporal_memory
70 .push_back(current_temporal.clone());
71
72 while advancedstate.temporal_memory.len() > config.temporal_window {
74 advancedstate.temporal_memory.pop_front();
75 }
76
77 if advancedstate.temporal_memory.len() >= config.causal_depth {
79 for y in 0..height {
80 for x in 0..width {
81 let pixel_id = y * width + x;
82
83 let temporal_sequence =
85 extract_pixel_temporal_sequence(&advancedstate.temporal_memory, (y, x))?;
86
87 let causal_relationships =
89 detect_causal_relationships(&temporal_sequence, pixel_id, config)?;
90
91 advancedstate
93 .causal_graph
94 .insert(pixel_id, causal_relationships.clone());
95
96 let causal_influence = calculate_causal_influence(
98 &causal_relationships,
99 &advancedstate.causal_graph,
100 config,
101 )?;
102
103 causal_output[(y, x)] = causal_influence;
104 }
105 }
106 }
107
108 Ok(causal_output)
109}
110
111#[allow(dead_code)]
123fn image_to_temporal_representation<T>(image: &ArrayView2<T>) -> NdimageResult<Array3<f64>>
124where
125 T: Float + FromPrimitive + Copy,
126{
127 let (height, width) = image.dim();
128 let mut temporal_features = Array3::zeros((height, width, 4));
129
130 for y in 0..height {
131 for x in 0..width {
132 let pixel_val = image[(y, x)].to_f64().unwrap_or(0.0);
133
134 temporal_features[(y, x, 0)] = pixel_val;
136
137 let h_grad = if x > 0 && x < width - 1 {
139 let left = image[(y, x - 1)].to_f64().unwrap_or(0.0);
140 let right = image[(y, x + 1)].to_f64().unwrap_or(0.0);
141 (right - left) / 2.0
142 } else {
143 0.0
144 };
145 temporal_features[(y, x, 1)] = h_grad;
146
147 let v_grad = if y > 0 && y < height - 1 {
149 let up = image[(y - 1, x)].to_f64().unwrap_or(0.0);
150 let down = image[(y + 1, x)].to_f64().unwrap_or(0.0);
151 (down - up) / 2.0
152 } else {
153 0.0
154 };
155 temporal_features[(y, x, 2)] = v_grad;
156
157 let mut local_vals = Vec::new();
159 for dy in -1..=1i32 {
160 for dx in -1..=1i32 {
161 let ny = y as i32 + dy;
162 let nx = x as i32 + dx;
163 if ny >= 0 && ny < height as i32 && nx >= 0 && nx < width as i32 {
164 let val = image[(ny as usize, nx as usize)].to_f64().unwrap_or(0.0);
165 local_vals.push(val);
166 }
167 }
168 }
169
170 let local_variance = if local_vals.len() > 1 {
171 let mean = local_vals.iter().sum::<f64>() / local_vals.len() as f64;
172 local_vals.iter().map(|&v| (v - mean).powi(2)).sum::<f64>()
173 / local_vals.len() as f64
174 } else {
175 0.0
176 };
177 temporal_features[(y, x, 3)] = local_variance;
178 }
179 }
180
181 Ok(temporal_features)
182}
183
184#[allow(dead_code)]
196fn extract_pixel_temporal_sequence(
197 temporal_memory: &VecDeque<Array3<f64>>,
198 position: (usize, usize),
199) -> NdimageResult<Vec<f64>> {
200 let (y, x) = position;
201 let mut sequence = Vec::new();
202
203 for frame in temporal_memory {
204 if y < frame.shape()[0] && x < frame.shape()[1] {
205 let mut pixel_features = Vec::new();
207 for feature_idx in 0..frame.shape()[2] {
208 pixel_features.push(frame[(y, x, feature_idx)]);
209 }
210
211 let combined_value = pixel_features
213 .iter()
214 .enumerate()
215 .map(|(i, &val)| val * (i as f64 + 1.0) / pixel_features.len() as f64)
216 .sum::<f64>();
217
218 sequence.push(combined_value);
219 }
220 }
221
222 while sequence.len() < 8 {
224 sequence.push(0.0);
225 }
226
227 Ok(sequence)
228}
229
230#[allow(dead_code)]
244fn detect_causal_relationships(
245 temporal_sequence: &[f64],
246 pixel_id: usize,
247 config: &AdvancedConfig,
248) -> NdimageResult<Vec<CausalRelation>> {
249 let mut causal_relations = Vec::new();
250
251 if temporal_sequence.len() < config.causal_depth {
252 return Ok(causal_relations);
253 }
254
255 for delay in 1..config.causal_depth.min(temporal_sequence.len() / 2) {
257 let mut cause_values = Vec::new();
258 let mut effect_values = Vec::new();
259
260 for i in delay..temporal_sequence.len() {
261 cause_values.push(temporal_sequence[i - delay]);
262 effect_values.push(temporal_sequence[i]);
263 }
264
265 if cause_values.len() < 3 {
266 continue;
267 }
268
269 let cause_mean = cause_values.iter().sum::<f64>() / cause_values.len() as f64;
271 let effect_mean = effect_values.iter().sum::<f64>() / effect_values.len() as f64;
272
273 let numerator: f64 = cause_values
274 .iter()
275 .zip(effect_values.iter())
276 .map(|(&c, &e)| (c - cause_mean) * (e - effect_mean))
277 .sum();
278
279 let cause_var: f64 = cause_values.iter().map(|&c| (c - cause_mean).powi(2)).sum();
280
281 let effect_var: f64 = effect_values
282 .iter()
283 .map(|&e| (e - effect_mean).powi(2))
284 .sum();
285
286 let denominator = (cause_var * effect_var).sqrt();
287
288 if denominator > 1e-10 {
289 let correlation = numerator / denominator;
290 let causal_strength = correlation.abs();
291
292 if causal_strength > 0.3 {
294 let confidence =
296 (causal_strength * (cause_values.len() as f64).ln() / 10.0).min(1.0);
297
298 let target_id = if correlation > 0.0 {
300 pixel_id + delay } else {
302 if pixel_id >= delay {
303 pixel_id - delay
304 } else {
305 pixel_id
306 } };
308
309 causal_relations.push(CausalRelation {
310 source: pixel_id,
311 target: target_id,
312 strength: causal_strength,
313 delay,
314 confidence,
315 });
316 }
317 }
318 }
319
320 for window_size in 2..=(config.causal_depth / 2).min(temporal_sequence.len() / 4) {
322 if temporal_sequence.len() < window_size * 2 {
323 continue;
324 }
325
326 let mut entropy_source = 0.0;
328 let mut entropy_target = 0.0;
329 let mut mutual_entropy = 0.0;
330
331 for i in window_size..temporal_sequence.len() - window_size {
332 let source_window = &temporal_sequence[i - window_size..i];
333 let target_window = &temporal_sequence[i..i + window_size];
334
335 let source_var = calculate_window_variance(source_window);
337 let target_var = calculate_window_variance(target_window);
338
339 entropy_source += source_var;
340 entropy_target += target_var;
341
342 let cross_corr = source_window
344 .iter()
345 .zip(target_window.iter())
346 .map(|(&s, &t)| s * t)
347 .sum::<f64>()
348 / window_size as f64;
349
350 mutual_entropy += cross_corr.abs();
351 }
352
353 let n_windows = (temporal_sequence.len() - window_size * 2) as f64;
354 if n_windows > 0.0 {
355 entropy_source /= n_windows;
356 entropy_target /= n_windows;
357 mutual_entropy /= n_windows;
358
359 let transfer_entropy = mutual_entropy / (entropy_source + entropy_target + 1e-10);
361
362 if transfer_entropy > 0.2 {
363 let confidence = (transfer_entropy * n_windows.ln() / 5.0).min(1.0);
364
365 causal_relations.push(CausalRelation {
366 source: pixel_id,
367 target: pixel_id + window_size, strength: transfer_entropy,
369 delay: window_size,
370 confidence,
371 });
372 }
373 }
374 }
375
376 causal_relations.sort_by(|a, b| {
378 b.strength
379 .partial_cmp(&a.strength)
380 .unwrap_or(Ordering::Equal)
381 });
382 causal_relations.truncate(config.causal_depth / 2);
383
384 Ok(causal_relations)
385}
386
387#[allow(dead_code)]
401fn calculate_causal_influence(
402 relationships: &[CausalRelation],
403 causal_graph: &BTreeMap<usize, Vec<CausalRelation>>,
404 _config: &AdvancedConfig,
405) -> NdimageResult<f64> {
406 if relationships.is_empty() {
407 return Ok(0.0);
408 }
409
410 let direct_influence: f64 = relationships
412 .iter()
413 .map(|rel| rel.strength * rel.confidence)
414 .sum();
415
416 let mut indirect_influence = 0.0;
418 for relation in relationships {
419 if let Some(target_relations) = causal_graph.get(&relation.target) {
420 for target_rel in target_relations {
421 let distance_weight = 1.0 / (relation.delay as f64 + 1.0);
423 indirect_influence +=
424 target_rel.strength * target_rel.confidence * distance_weight * 0.5;
425 }
426 }
427 }
428
429 let total_influence = direct_influence + indirect_influence;
431
432 let normalized_influence = total_influence / (1.0 + total_influence);
434
435 Ok(normalized_influence)
436}
437
438#[allow(dead_code)]
449fn calculate_window_variance(window: &[f64]) -> f64 {
450 if window.is_empty() {
451 return 0.0;
452 }
453
454 let mean = window.iter().sum::<f64>() / window.len() as f64;
455 let variance = window.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / window.len() as f64;
456
457 variance
458}
459
460#[allow(dead_code)]
472pub fn analyze_multiscale_temporal_causality(
473 temporal_memory: &VecDeque<Array3<f64>>,
474 config: &AdvancedConfig,
475) -> NdimageResult<Vec<Array2<f64>>> {
476 let mut scales_results = Vec::new();
477
478 if temporal_memory.is_empty() {
479 return Ok(scales_results);
480 }
481
482 let (height, width, _) = temporal_memory[0].dim();
483
484 for scale in 1..=3 {
486 let mut scale_result = Array2::zeros((height, width));
487 let step_size = scale * 2;
488
489 let mut scaled_memory = VecDeque::new();
491 for (i, frame) in temporal_memory.iter().enumerate() {
492 if i % step_size == 0 {
493 scaled_memory.push_back(frame.clone());
494 }
495 }
496
497 if scaled_memory.len() >= config.causal_depth / scale {
498 for y in 0..height {
499 for x in 0..width {
500 if let Ok(temporal_sequence) =
502 extract_pixel_temporal_sequence(&scaled_memory, (y, x))
503 {
504 let pixel_id = y * width + x;
505
506 if let Ok(relationships) =
508 detect_causal_relationships(&temporal_sequence, pixel_id, config)
509 {
510 let causal_strength: f64 = relationships
511 .iter()
512 .map(|rel| rel.strength * rel.confidence)
513 .sum();
514
515 scale_result[(y, x)] = causal_strength / (scale as f64);
516 }
517 }
518 }
519 }
520 }
521
522 scales_results.push(scale_result);
523 }
524
525 Ok(scales_results)
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531 use scirs2_core::ndarray::Array2;
532
533 #[test]
534 fn test_calculate_window_variance() {
535 let window = vec![1.0, 2.0, 3.0, 4.0, 5.0];
536 let variance = calculate_window_variance(&window);
537 assert!(variance > 0.0);
538
539 let empty_window = vec![];
541 let zero_variance = calculate_window_variance(&empty_window);
542 assert_eq!(zero_variance, 0.0);
543 }
544
545 #[test]
546 fn test_image_to_temporal_representation() {
547 let image = Array2::<f64>::zeros((10, 10));
548 let result = image_to_temporal_representation(&image.view());
549 assert!(result.is_ok());
550
551 let temporal_features = result.unwrap();
552 assert_eq!(temporal_features.dim(), (10, 10, 4));
553 }
554
555 #[test]
556 fn test_extract_pixel_temporal_sequence() {
557 let mut temporal_memory = VecDeque::new();
558 let frame1 = Array3::<f64>::zeros((5, 5, 4));
559 let frame2 = Array3::<f64>::ones((5, 5, 4));
560
561 temporal_memory.push_back(frame1);
562 temporal_memory.push_back(frame2);
563
564 let result = extract_pixel_temporal_sequence(&temporal_memory, (2, 2));
565 assert!(result.is_ok());
566
567 let sequence = result.unwrap();
568 assert!(!sequence.is_empty());
569 }
570}