1use super::*;
23use crate::adaptive::hyperbolic::{RoutingStats, angle_difference};
24use async_trait::async_trait;
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27use std::sync::Arc;
28use tokio::sync::RwLock;
29
30const FIXED_POINT_SCALE: i64 = 1_000_000; const HYSTERESIS_THRESHOLD: f64 = 0.001;
35const HYSTERESIS_DAMPING: f64 = 0.8;
36
37#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
39pub struct EnhancedHyperbolicCoordinate {
40 r_fixed: i64,
42 theta_fixed: i64,
44}
45
46impl EnhancedHyperbolicCoordinate {
47 pub fn from_float(r: f64, theta: f64) -> Self {
49 Self {
50 r_fixed: (r * FIXED_POINT_SCALE as f64) as i64,
51 theta_fixed: (theta * FIXED_POINT_SCALE as f64) as i64,
52 }
53 }
54
55 pub fn r(&self) -> f64 {
57 self.r_fixed as f64 / FIXED_POINT_SCALE as f64
58 }
59
60 pub fn theta(&self) -> f64 {
62 self.theta_fixed as f64 / FIXED_POINT_SCALE as f64
63 }
64
65 pub fn set_r(&mut self, r: f64) {
67 self.r_fixed = (r * FIXED_POINT_SCALE as f64) as i64;
68 }
69
70 pub fn set_theta(&mut self, theta: f64) {
72 self.theta_fixed = (theta * FIXED_POINT_SCALE as f64) as i64;
73 }
74}
75
76pub struct EnhancedHyperbolicSpace {
78 my_coordinate: RwLock<EnhancedHyperbolicCoordinate>,
80
81 previous_coordinate: RwLock<Option<EnhancedHyperbolicCoordinate>>,
83
84 neighbor_coordinates: Arc<RwLock<HashMap<NodeId, EnhancedHyperbolicCoordinate>>>,
86
87 adjustment_params: AdjustmentParameters,
89
90 _routing_stats: Arc<RwLock<RoutingStats>>,
92
93 visualization_data: Arc<RwLock<VisualizationData>>,
95}
96
97#[derive(Debug, Clone)]
99pub struct AdjustmentParameters {
100 base_rate: f64,
102 current_rate: f64,
104 min_rate: f64,
106 max_rate: f64,
108 hysteresis_factor: f64,
110}
111
112impl Default for AdjustmentParameters {
113 fn default() -> Self {
114 Self {
115 base_rate: 0.01,
116 current_rate: 0.01,
117 min_rate: 0.001,
118 max_rate: 0.1,
119 hysteresis_factor: HYSTERESIS_DAMPING,
120 }
121 }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct VisualizationData {
127 pub nodes: HashMap<NodeId, VisualizationNode>,
129 pub paths: Vec<RoutingPath>,
131 pub metrics: NetworkMetrics,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct VisualizationNode {
137 pub id: NodeId,
138 pub coordinate: HyperbolicCoordinate,
139 pub label: String,
140 pub degree: usize,
141 pub trust_score: f64,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct RoutingPath {
146 pub source: NodeId,
147 pub target: NodeId,
148 pub hops: Vec<NodeId>,
149 pub success: bool,
150 pub total_distance: f64,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct NetworkMetrics {
155 pub total_nodes: usize,
156 pub average_degree: f64,
157 pub clustering_coefficient: f64,
158 pub average_path_length: f64,
159}
160
161impl Default for EnhancedHyperbolicSpace {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167impl EnhancedHyperbolicSpace {
168 pub fn new() -> Self {
170 let initial_r = 0.5;
171 let initial_theta = rand::random::<f64>() * 2.0 * std::f64::consts::PI;
172
173 Self {
174 my_coordinate: RwLock::new(EnhancedHyperbolicCoordinate::from_float(
175 initial_r,
176 initial_theta,
177 )),
178 previous_coordinate: RwLock::new(None),
179 neighbor_coordinates: Arc::new(RwLock::new(HashMap::new())),
180 adjustment_params: AdjustmentParameters::default(),
181 _routing_stats: Arc::new(RwLock::new(RoutingStats::default())),
182 visualization_data: Arc::new(RwLock::new(VisualizationData {
183 nodes: HashMap::new(),
184 paths: Vec::new(),
185 metrics: NetworkMetrics {
186 total_nodes: 0,
187 average_degree: 0.0,
188 clustering_coefficient: 0.0,
189 average_path_length: 0.0,
190 },
191 })),
192 }
193 }
194
195 pub fn distance_fixed(
197 a: &EnhancedHyperbolicCoordinate,
198 b: &EnhancedHyperbolicCoordinate,
199 ) -> f64 {
200 let r1 = a.r();
201 let r2 = b.r();
202 let theta1 = a.theta();
203 let theta2 = b.theta();
204
205 let cos_angle_diff = (theta1 - theta2).cos();
207
208 if r1 > 0.999 || r2 > 0.999 {
210 return f64::INFINITY;
211 }
212
213 let numerator = (r1 - r2).powi(2) + 4.0 * r1 * r2 * (1.0 - cos_angle_diff);
215 let denominator = (1.0 - r1.powi(2)) * (1.0 - r2.powi(2));
216
217 if denominator <= 0.0 {
218 return f64::INFINITY;
219 }
220
221 let cosh_dist = 1.0 + numerator / denominator;
222 cosh_dist.acosh()
223 }
224
225 pub async fn adjust_coordinate_with_hysteresis(
227 &self,
228 neighbor_coords: &[(NodeId, EnhancedHyperbolicCoordinate)],
229 ) {
230 let mut my_coord = self.my_coordinate.write().await;
231 let mut prev_coord_guard = self.previous_coordinate.write().await;
232
233 let degree = neighbor_coords.len();
235 let target_r = 1.0 - (2.0 / (degree as f64 + 2.0));
236
237 let (sin_sum, cos_sum) =
239 neighbor_coords
240 .iter()
241 .fold((0.0, 0.0), |(sin_acc, cos_acc), (_, coord)| {
242 let theta = coord.theta();
243 (sin_acc + theta.sin(), cos_acc + theta.cos())
244 });
245
246 let target_theta = sin_sum.atan2(cos_sum);
247
248 let mut params = self.adjustment_params.clone();
250
251 if let Some(prev_coord) = prev_coord_guard.as_ref() {
252 let prev_movement = Self::distance_fixed(&my_coord, prev_coord);
253
254 if prev_movement < HYSTERESIS_THRESHOLD {
256 params.current_rate *= params.hysteresis_factor;
258 } else {
259 params.current_rate = params.base_rate;
261 }
262
263 params.current_rate = params.current_rate.clamp(params.min_rate, params.max_rate);
265 }
266
267 *prev_coord_guard = Some(*my_coord);
269
270 let current_r = my_coord.r();
272 let current_theta = my_coord.theta();
273
274 let new_r = current_r + params.current_rate * (target_r - current_r);
275 let angle_diff = angle_difference(target_theta, current_theta);
276 let new_theta = current_theta + params.current_rate * angle_diff;
277
278 my_coord.set_r(new_r.clamp(0.0, 0.999));
280
281 let normalized_theta = if new_theta < 0.0 {
283 new_theta + 2.0 * std::f64::consts::PI
284 } else if new_theta >= 2.0 * std::f64::consts::PI {
285 new_theta - 2.0 * std::f64::consts::PI
286 } else {
287 new_theta
288 };
289
290 my_coord.set_theta(normalized_theta);
291 }
292
293 pub async fn update_visualization(&self) {
295 let neighbors = self.neighbor_coordinates.read().await;
296 let mut viz_data = self.visualization_data.write().await;
297
298 viz_data.nodes.clear();
300
301 let my_coord = self.my_coordinate.read().await;
303 let my_id = generate_local_node_id(); viz_data.nodes.insert(
306 my_id.clone(),
307 VisualizationNode {
308 id: my_id.clone(),
309 coordinate: HyperbolicCoordinate {
310 r: my_coord.r(),
311 theta: my_coord.theta(),
312 },
313 label: "Local Node".to_string(),
314 degree: neighbors.len(),
315 trust_score: 1.0,
316 },
317 );
318
319 for (node_id, coord) in neighbors.iter() {
321 viz_data.nodes.insert(
322 node_id.clone(),
323 VisualizationNode {
324 id: node_id.clone(),
325 coordinate: HyperbolicCoordinate {
326 r: coord.r(),
327 theta: coord.theta(),
328 },
329 label: format!("Node {:?}", node_id),
330 degree: 0, trust_score: 0.5, },
333 );
334 }
335
336 viz_data.metrics.total_nodes = viz_data.nodes.len();
338 viz_data.metrics.average_degree = neighbors.len() as f64;
339 }
340
341 pub async fn export_visualization_json(&self) -> Result<String> {
343 let viz_data = self.visualization_data.read().await;
344 use std::collections::BTreeMap;
346 let mut nodes_map: BTreeMap<String, VisualizationNode> = BTreeMap::new();
347 for (k, v) in viz_data.nodes.iter() {
348 nodes_map.insert(format!("{:?}", k), v.clone());
349 }
350 let export = serde_json::json!({
351 "nodes": nodes_map,
352 "metrics": viz_data.metrics,
353 });
354
355 serde_json::to_string_pretty(&export).map_err(|e| {
356 AdaptiveNetworkError::Other(format!("Failed to serialize visualization: {}", e))
357 })
358 }
359
360 pub async fn export_visualization_svg(&self) -> Result<String> {
362 let viz_data = self.visualization_data.read().await;
363 let mut svg = String::new();
364
365 svg.push_str(
367 r#"<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">
368 <circle cx="400" cy="400" r="380" fill="none" stroke="black" stroke-width="2"/>
369 "#,
370 );
371
372 for node in viz_data.nodes.values() {
374 let (x, y) = polar_to_cartesian(
375 node.coordinate.r,
376 node.coordinate.theta,
377 400.0,
378 400.0,
379 380.0,
380 );
381 svg.push_str(&format!(
382 r#"<circle cx="{}" cy="{}" r="5" fill="blue" stroke="black" stroke-width="1"/>"#,
383 x, y
384 ));
385 }
386
387 for path in &viz_data.paths {
389 if let (Some(source), Some(target)) = (
390 viz_data.nodes.get(&path.source),
391 viz_data.nodes.get(&path.target),
392 ) {
393 let (x1, y1) = polar_to_cartesian(
394 source.coordinate.r,
395 source.coordinate.theta,
396 400.0,
397 400.0,
398 380.0,
399 );
400 let (x2, y2) = polar_to_cartesian(
401 target.coordinate.r,
402 target.coordinate.theta,
403 400.0,
404 400.0,
405 380.0,
406 );
407
408 let color = if path.success { "green" } else { "red" };
409 svg.push_str(&format!(
410 r#"<line x1="{}" y1="{}" x2="{}" y2="{}" stroke="{}" stroke-width="2" opacity="0.5"/>"#,
411 x1, y1, x2, y2, color
412 ));
413 }
414 }
415
416 svg.push_str("</svg>");
417 Ok(svg)
418 }
419}
420
421fn polar_to_cartesian(r: f64, theta: f64, cx: f64, cy: f64, radius: f64) -> (f64, f64) {
423 let x = cx + r * radius * theta.cos();
424 let y = cy + r * radius * theta.sin();
425 (x, y)
426}
427
428fn generate_local_node_id() -> NodeId {
430 use rand::RngCore;
431 let mut hash = [0u8; 32];
432 rand::thread_rng().fill_bytes(&mut hash);
433 NodeId::from_bytes(hash)
434}
435
436pub struct EnhancedHyperbolicRoutingStrategy {
438 space: Arc<EnhancedHyperbolicSpace>,
440
441 local_id: NodeId,
443
444 max_hops: usize,
446
447 enable_visualization: bool,
449}
450
451impl EnhancedHyperbolicRoutingStrategy {
452 pub fn new(local_id: NodeId, space: Arc<EnhancedHyperbolicSpace>) -> Self {
454 Self {
455 space,
456 local_id,
457 max_hops: 10,
458 enable_visualization: true,
459 }
460 }
461
462 async fn find_enhanced_path(&self, target: &NodeId) -> Result<Vec<NodeId>> {
464 let target_coord = {
465 let neighbors = self.space.neighbor_coordinates.read().await;
466 neighbors.get(target).cloned()
467 };
468
469 let target_coord = match target_coord {
470 Some(coord) => coord,
471 None => {
472 return Err(AdaptiveNetworkError::Routing(
473 "Target coordinate unknown".to_string(),
474 ));
475 }
476 };
477
478 let mut path = Vec::new();
479 let mut visited = std::collections::HashSet::<NodeId>::new();
480 visited.insert(self.local_id.clone());
481
482 let mut total_distance = 0.0;
483 let _start_time = std::time::Instant::now();
484
485 for hop in 0..self.max_hops {
487 let my_coord = self.space.my_coordinate.read().await;
488 let my_distance = EnhancedHyperbolicSpace::distance_fixed(&my_coord, &target_coord);
489
490 let neighbors = self.space.neighbor_coordinates.read().await;
491 let next_hop = neighbors
492 .iter()
493 .filter(|(id, _)| !visited.contains(id))
494 .filter(|(_, coord)| {
495 EnhancedHyperbolicSpace::distance_fixed(coord, &target_coord) < my_distance
496 })
497 .min_by(|(_, a), (_, b)| {
498 let dist_a = EnhancedHyperbolicSpace::distance_fixed(a, &target_coord);
499 let dist_b = EnhancedHyperbolicSpace::distance_fixed(b, &target_coord);
500 dist_a
501 .partial_cmp(&dist_b)
502 .unwrap_or(std::cmp::Ordering::Equal)
503 })
504 .map(|(id, coord)| (id.clone(), *coord));
505
506 match next_hop {
507 Some((next_id, next_coord)) => {
508 if next_id == *target {
509 path.push(next_id);
511 total_distance +=
512 EnhancedHyperbolicSpace::distance_fixed(&my_coord, &next_coord);
513
514 if self.enable_visualization {
516 let mut viz_data = self.space.visualization_data.write().await;
517 viz_data.paths.push(RoutingPath {
518 source: self.local_id.clone(),
519 target: target.clone(),
520 hops: path.clone(),
521 success: true,
522 total_distance,
523 });
524 }
525
526 return Ok(path);
527 }
528
529 path.push(next_id.clone());
530 visited.insert(next_id);
531 total_distance +=
532 EnhancedHyperbolicSpace::distance_fixed(&my_coord, &next_coord);
533 }
534 None => {
535 if self.enable_visualization {
537 let mut viz_data = self.space.visualization_data.write().await;
538 viz_data.paths.push(RoutingPath {
539 source: self.local_id.clone(),
540 target: target.clone(),
541 hops: path.clone(),
542 success: false,
543 total_distance,
544 });
545 }
546
547 return Err(AdaptiveNetworkError::Routing(format!(
548 "No closer neighbor found after {} hops",
549 hop
550 )));
551 }
552 }
553 }
554
555 Err(AdaptiveNetworkError::Routing(
557 "Maximum hop count exceeded".to_string(),
558 ))
559 }
560}
561
562#[async_trait]
563impl RoutingStrategy for EnhancedHyperbolicRoutingStrategy {
564 async fn find_path(&self, target: &NodeId) -> Result<Vec<NodeId>> {
565 self.find_enhanced_path(target).await
566 }
567
568 fn route_score(&self, _neighbor: &NodeId, _target: &NodeId) -> f64 {
569 0.5
570 }
571
572 fn update_metrics(&mut self, _path: &[NodeId], _success: bool) {
573 }
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580
581 #[test]
582 fn test_fixed_point_conversion() {
583 let coord = EnhancedHyperbolicCoordinate::from_float(0.5, std::f64::consts::PI);
584 assert!((coord.r() - 0.5).abs() < 1e-6);
585 assert!((coord.theta() - std::f64::consts::PI).abs() < 1e-6);
586 }
587
588 #[test]
589 fn test_enhanced_distance_calculation() {
590 let a = EnhancedHyperbolicCoordinate::from_float(0.0, 0.0);
591 let b = EnhancedHyperbolicCoordinate::from_float(0.5, std::f64::consts::PI);
592
593 let dist = EnhancedHyperbolicSpace::distance_fixed(&a, &b);
594 assert!(dist > 0.0);
595 assert!(dist.is_finite());
596
597 let boundary = EnhancedHyperbolicCoordinate::from_float(0.9999, 0.0);
599 let dist_boundary = EnhancedHyperbolicSpace::distance_fixed(&a, &boundary);
600 assert!(dist_boundary > 5.0); }
602
603 #[tokio::test]
604 async fn test_hysteresis_adjustment() {
605 let space = EnhancedHyperbolicSpace::new();
606
607 let neighbors = vec![
609 (
610 generate_local_node_id(),
611 EnhancedHyperbolicCoordinate::from_float(0.8, 0.0),
612 ),
613 (
614 generate_local_node_id(),
615 EnhancedHyperbolicCoordinate::from_float(0.8, std::f64::consts::PI),
616 ),
617 ];
618
619 let mut movements = vec![];
621 for _i in 0..10 {
622 let coord_before = *space.my_coordinate.read().await;
623 space.adjust_coordinate_with_hysteresis(&neighbors).await;
624 let coord_after = *space.my_coordinate.read().await;
625
626 let movement = EnhancedHyperbolicSpace::distance_fixed(&coord_before, &coord_after);
627 movements.push(movement);
628
629 tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
631 }
632
633 for i in 1..movements.len() {
635 assert!(movements[i] <= movements[i - 1] * 1.1); }
637 }
638
639 #[tokio::test]
640 async fn test_visualization_export() {
641 let space = Arc::new(EnhancedHyperbolicSpace::new());
642
643 let mut neighbors = space.neighbor_coordinates.write().await;
645 for i in 0..5 {
646 let angle = (i as f64) * 2.0 * std::f64::consts::PI / 5.0;
647 neighbors.insert(
648 generate_local_node_id(),
649 EnhancedHyperbolicCoordinate::from_float(0.5, angle),
650 );
651 }
652 drop(neighbors);
653
654 space.update_visualization().await;
656
657 let json = space.export_visualization_json().await.unwrap();
659 assert!(json.contains("nodes"));
660 assert!(json.contains("metrics"));
661
662 let svg = space.export_visualization_svg().await.unwrap();
664 assert!(svg.contains("<svg"));
665 assert!(svg.contains("<circle"));
666 }
667}