diff --git a/src/algorithm/bellande_node_importance.rs b/src/algorithm/bellande_node_importance.rs index 68a0a2d..c6eed34 100644 --- a/src/algorithm/bellande_node_importance.rs +++ b/src/algorithm/bellande_node_importance.rs @@ -13,5 +13,292 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::algorithm::connections::BellandeArchError; +use crate::algorithm::connections::{euclidean_distance, BellandeArchError}; use bellande_node_importance::make_bellande_node_importance_request; +use futures::future::join_all; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Configuration for node importance evaluation using Bellande Node Importance algorithm +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NodeImportanceConfig { + pub node_coordinates: Vec, + pub segment_id: String, + pub recent_nodes: Vec>, + pub important_nodes_by_segment: std::collections::HashMap>>, + pub adjacent_segments: std::collections::HashMap>, + pub grid_steps: Vec, + pub min_segment_coverage: f64, + pub use_local_executable: bool, +} + +/// Result of a node importance evaluation operation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NodeImportanceResult { + pub importance_score: f64, + pub coverage_metrics: Option>, + pub segment_coverage: Option, + pub execution_time_ms: Option, + pub node_attributes: Option>, +} + +/// Extended configuration for advanced node importance evaluations +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdvancedNodeImportanceConfig { + pub basic_config: NodeImportanceConfig, + pub priority_regions: Option>>>, + pub coverage_bias: Option, + pub weighting_factor: Option, +} + +/// Main node importance evaluation module that leverages the Bellande Node Importance algorithm +pub struct NodeImportanceEvaluator; + +impl NodeImportanceEvaluator { + /// Evaluates the importance of a node using the Bellande Node Importance algorithm + pub async fn evaluate( + config: NodeImportanceConfig, + ) -> Result { + // Validate input coordinates + if config.node_coordinates.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Node coordinates cannot be empty".to_string(), + )); + } + + // Validate segment ID + if config.segment_id.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Segment ID cannot be empty".to_string(), + )); + } + + // Validate grid steps + if config.grid_steps.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Grid steps cannot be empty".to_string(), + )); + } + + let dimensions = config.node_coordinates.len(); + + // Verify that grid steps have the same dimensions as node coordinates + if dimensions != config.grid_steps.len() { + return Err(BellandeArchError::DimensionMismatch(format!( + "Coordinate dimension mismatch: node={}, grid_steps={}", + dimensions, + config.grid_steps.len() + ))); + } + + // Create the node JSON structure + let node = serde_json::json!({ + "coords": config.node_coordinates, + "segment": config.segment_id + }); + + // Convert recent nodes to JSON Value + let recent_nodes = serde_json::json!(config.recent_nodes); + + // Convert important nodes to JSON Value + let important_nodes = serde_json::json!(config.important_nodes_by_segment); + + // Convert adjacent segments to JSON Value + let adjacent_segments = serde_json::json!(config.adjacent_segments); + + // Convert grid steps to JSON Value + let grid_steps = serde_json::json!(config.grid_steps); + + // Run the Bellande Node Importance algorithm using the API + let result = if !config.use_local_executable { + make_bellande_node_importance_request( + node, + recent_nodes, + important_nodes, + adjacent_segments, + grid_steps, + config.min_segment_coverage, + ) + .await + .map_err(|e| BellandeArchError::AlgorithmError(e.to_string()))? + } else { + return Err(BellandeArchError::AlgorithmError( + "Local executable functionality not implemented".to_string(), + )); + }; + + // Process and transform the results + Self::process_result(result) + } + + /// Process the raw Bellande Node Importance result into our domain-specific format + pub fn process_result(result: Value) -> Result { + // Extract the importance score from the result + let importance_score = result + .get("importance_score") + .and_then(|s| s.as_f64()) + .ok_or_else(|| { + BellandeArchError::AlgorithmError( + "Missing importance score in algorithm result".to_string(), + ) + })?; + + // Extract coverage metrics if available + let coverage_metrics = result + .get("coverage_metrics") + .and_then(|m| m.as_array()) + .map(|metrics| { + metrics + .iter() + .filter_map(|v| v.as_f64()) + .collect::>() + }); + + // Extract segment coverage if available + let segment_coverage = result.get("segment_coverage").and_then(|c| c.as_f64()); + + // Extract execution time if available + let execution_time_ms = result + .get("execution_time_ms") + .and_then(|t| t.as_i64()) + .map(|t| t as i32); + + // Extract node attributes if available + let node_attributes = result + .get("node_attributes") + .and_then(|a| a.as_object()) + .map(|obj| { + obj.iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>() + }); + + Ok(NodeImportanceResult { + importance_score, + coverage_metrics, + segment_coverage, + execution_time_ms, + node_attributes, + }) + } + + /// Evaluates node importance with optimal parameters based on segment characteristics + pub async fn evaluate_optimal( + node_coords: Vec, + segment_id: String, + recent_nodes: Vec>, + important_nodes_by_segment: std::collections::HashMap>>, + adjacent_segments: std::collections::HashMap>, + ) -> Result { + // Validate inputs + if node_coords.is_empty() || segment_id.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Node coordinates and segment ID cannot be empty".to_string(), + )); + } + + let dimensions = node_coords.len(); + + // Determine appropriate grid steps based on segment characteristics + let grid_steps = if let Some(segment_nodes) = important_nodes_by_segment.get(&segment_id) { + if !segment_nodes.is_empty() { + // Calculate average distance between nodes in this segment + let mut total_distance = 0.0; + let mut count = 0; + + for i in 0..segment_nodes.len() { + for j in i + 1..segment_nodes.len() { + if let Ok(dist) = euclidean_distance(&segment_nodes[i], &segment_nodes[j]) { + total_distance += dist; + count += 1; + } + } + } + + let avg_distance = if count > 0 { + total_distance / count as f64 + } else { + 1.0 + }; + // Use 10% of average distance as grid step + vec![avg_distance * 0.1; dimensions] + } else { + // Default grid steps if no nodes in segment + vec![1.0; dimensions] + } + } else { + // Default grid steps if segment not found + vec![1.0; dimensions] + }; + + // Determine minimum segment coverage based on adjacent segments count + let adjacent_count = adjacent_segments + .get(&segment_id) + .map(|adj| adj.len()) + .unwrap_or(0); + + let min_segment_coverage = match adjacent_count { + 0 => 0.7, // Isolated segment, require higher coverage + 1..=2 => 0.6, // Few connections + 3..=5 => 0.5, // Moderate connections + _ => 0.4, // Many connections, can be more flexible + }; + + // Basic configuration + let config = NodeImportanceConfig { + node_coordinates: node_coords, + segment_id, + recent_nodes, + important_nodes_by_segment, + adjacent_segments, + grid_steps, + min_segment_coverage, + use_local_executable: false, + }; + + // Perform evaluation + Self::evaluate(config).await + } + + /// Batch processes multiple node importance evaluations in parallel + pub async fn batch_evaluate( + configs: Vec, + ) -> Vec> { + // Process each configuration in parallel + let futures = configs.into_iter().map(|config| Self::evaluate(config)); + + // Collect all results + join_all(futures).await + } + + /// Advanced batch processing with custom parameters for each evaluation + pub async fn advanced_batch_evaluate( + configs: Vec, + ) -> Vec> { + let futures = configs.into_iter().map(|config| { + // Use basic evaluation + Self::evaluate(config.basic_config) + }); + + join_all(futures).await + } + + /// Calculates a comparative importance score between two nodes + pub fn calculate_comparative_importance( + node1_result: &NodeImportanceResult, + node2_result: &NodeImportanceResult, + weighting_factor: f64, + ) -> f64 { + // Basic comparison of importance scores + let importance_diff = node1_result.importance_score - node2_result.importance_score; + + // Consider segment coverage if available + let coverage_factor = match (node1_result.segment_coverage, node2_result.segment_coverage) { + (Some(cov1), Some(cov2)) => (cov1 - cov2) * weighting_factor, + _ => 0.0, + }; + + // Combined score + importance_diff + coverage_factor + } +} diff --git a/src/algorithm/bellande_step.rs b/src/algorithm/bellande_step.rs index d36df90..ca286eb 100644 --- a/src/algorithm/bellande_step.rs +++ b/src/algorithm/bellande_step.rs @@ -14,8 +14,7 @@ // along with this program. If not, see . use crate::algorithm::connections::{ - euclidean_distance, is_point_valid, vector_dot_product, vector_length, vector_subtract, - BellandeArchError, + euclidean_distance, vector_dot_product, vector_length, vector_subtract, BellandeArchError, }; use bellande_step::make_bellande_step_request; use futures::future::join_all;