From c33aeda9d5d441bffb0d6921e0b539c2803b5fa4 Mon Sep 17 00:00:00 2001 From: RonaldsonBellande Date: Fri, 25 Apr 2025 09:59:24 -0400 Subject: [PATCH] update --- Cargo.toml | 2 +- src/algorithm/bellande_limit.rs | 436 ++++++++++++++++++++++ src/algorithm/bellande_node_importance.rs | 3 + src/algorithm/bellande_particle.rs | 3 + src/algorithm/bellande_probability.rs | 3 + src/algorithm/bellande_segment.rs | 3 + src/algorithm/bellande_tree.rs | 3 + src/mesh/architecture.rs | 22 ++ src/mesh/connections.rs | 0 src/mesh/mod.rs | 2 +- 10 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 src/mesh/architecture.rs delete mode 100644 src/mesh/connections.rs diff --git a/Cargo.toml b/Cargo.toml index 151a320..e7539b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ tokio-rustls = "0.24" # Bellande Architecture bellande_step = "0.1.1" bellande_limit = "0.1.2" -bellande_node_importance = "0.1.0" +bellande_node_importance = "0.1.3" bellande_particle = "0.1.1" bellande_probability = "0.1.1" # bellande_tree = "0.1.0" diff --git a/src/algorithm/bellande_limit.rs b/src/algorithm/bellande_limit.rs index 1aa944f..0e52a17 100644 --- a/src/algorithm/bellande_limit.rs +++ b/src/algorithm/bellande_limit.rs @@ -15,3 +15,439 @@ use crate::algorithm::connections::BellandeArchError; use bellande_limit::make_bellande_limit_request; +use futures::future::join_all; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// Configuration for spatial coordinate transformation using Bellande Limit algorithm +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SpatialLimitConfig { + pub start_coordinates: Vec, + pub end_coordinates: Vec, + pub environment_dimensions: Vec, + pub step_sizes: Vec, + pub goal_coordinates: Vec, + pub obstacles: Option>>, + pub search_radius: Option, + pub sample_points: Option, + pub use_local_executable: bool, +} + +/// Result of a spatial transformation operation using Bellande Limit +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LimitTransformationResult { + pub path_coordinates: Vec>, + pub convergence_metrics: Option>, + pub execution_time_ms: Option, + pub path_quality: Option, +} + +/// Extended configuration for advanced Bellande Limit transformations +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AdvancedLimitConfig { + pub basic_config: SpatialLimitConfig, + pub constraint_regions: Option>>>, + pub preferred_path_bias: Option, + pub smoothing_factor: Option, +} + +/// Main spatial transformation module that leverages the Bellande Limit algorithm +pub struct SpatialLimitTransformer; + +impl SpatialLimitTransformer { + /// Performs a spatial coordinate transformation using the Bellande Limit algorithm + pub async fn transform( + config: SpatialLimitConfig, + ) -> Result { + // Validate input coordinates + if config.start_coordinates.is_empty() || config.end_coordinates.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Coordinate vectors cannot be empty".to_string(), + )); + } + + // Validate environment dimensions + if config.environment_dimensions.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Environment dimensions cannot be empty".to_string(), + )); + } + + let dimensions = config.start_coordinates.len(); + + // Verify that all coordinate sets have the same dimensions + if dimensions != config.end_coordinates.len() + || dimensions != config.environment_dimensions.len() + || dimensions != config.step_sizes.len() + || dimensions != config.goal_coordinates.len() + { + return Err(BellandeArchError::DimensionMismatch(format!( + "Coordinate dimension mismatch: start={}, end={}, environment={}, size={}, goal={}", + dimensions, + config.end_coordinates.len(), + config.environment_dimensions.len(), + config.step_sizes.len(), + config.goal_coordinates.len() + ))); + } + + // Convert coordinates to JSON Values + let node0 = serde_json::json!(config.start_coordinates); + let node1 = serde_json::json!(config.end_coordinates); + let environment = serde_json::json!(config.environment_dimensions); + let size = serde_json::json!(config.step_sizes); + let goal = serde_json::json!(config.goal_coordinates); + let obstacles = config.obstacles.as_ref().map(|o| serde_json::json!(o)); + + // Set default values if not provided + let search_radius = config.search_radius.unwrap_or(50.0); + let sample_points = config.sample_points.unwrap_or(20); + + // Run the Bellande Limit algorithm using the API + let result = if !config.use_local_executable { + make_bellande_limit_request( + node0, + node1, + environment, + size, + goal, + obstacles, + search_radius, + sample_points, + ) + .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 Limit result into our domain-specific format + pub fn process_result(result: Value) -> Result { + // Extract the path data from the result + let path = result + .get("path") + .and_then(|p| p.as_array()) + .ok_or_else(|| { + BellandeArchError::AlgorithmError( + "Missing path data in algorithm result".to_string(), + ) + })?; + + // Convert the path to our format + let mut path_coordinates = Vec::new(); + for point in path { + let coords = point + .as_array() + .ok_or_else(|| { + BellandeArchError::AlgorithmError( + "Invalid point format in algorithm result".to_string(), + ) + })? + .iter() + .map(|v| { + v.as_f64().ok_or_else(|| { + BellandeArchError::AlgorithmError( + "Non-numeric coordinate in algorithm result".to_string(), + ) + }) + }) + .collect::, _>>()?; + + path_coordinates.push(coords); + } + + // Extract any metrics if available + let convergence_metrics = result + .get("convergence_metrics") + .and_then(|m| m.as_array()) + .map(|metrics| { + metrics + .iter() + .filter_map(|v| v.as_f64()) + .collect::>() + }); + + // 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 path quality if available + let path_quality = result.get("path_quality").and_then(|q| q.as_f64()); + + Ok(LimitTransformationResult { + path_coordinates, + convergence_metrics, + execution_time_ms, + path_quality, + }) + } + + /// Optimizes a path between two points considering environment constraints + pub async fn optimize_path( + start: Vec, + end: Vec, + environment: Vec, + obstacles: Vec>, + precision: f64, + ) -> Result { + // Validate inputs + if start.is_empty() || end.is_empty() || environment.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Start, end, and environment coordinates cannot be empty".to_string(), + )); + } + + if start.len() != end.len() || start.len() != environment.len() { + return Err(BellandeArchError::DimensionMismatch(format!( + "Dimension mismatch: start={}, end={}, environment={}", + start.len(), + end.len(), + environment.len() + ))); + } + + // Determine appropriate step sizes based on precision + let step_sizes = environment + .iter() + .map(|&dim| dim * precision / 100.0) + .collect::>(); + + // Use end coordinates as goal initially + let goal_coordinates = end.clone(); + + // Sample points based on precision + let sample_points = match precision { + p if p < 0.01 => 50, + p if p < 0.1 => 30, + _ => 20, + }; + + // Search radius based on environment size + let search_radius = environment.iter().sum::() / environment.len() as f64 * 0.1; + + // Basic configuration + let config = SpatialLimitConfig { + start_coordinates: start.clone(), + end_coordinates: end.clone(), + environment_dimensions: environment, + step_sizes, + goal_coordinates, + obstacles: Some(obstacles), + search_radius: Some(search_radius), + sample_points: Some(sample_points), + use_local_executable: false, + }; + + // Perform transformation + Self::transform(config).await + } + + /// Batch processes multiple transformations in parallel + pub async fn batch_transform( + configs: Vec, + ) -> Vec> { + // Process each configuration in parallel + let futures = configs.into_iter().map(|config| Self::transform(config)); + + // Collect all results + join_all(futures).await + } + + /// Advanced batch processing with custom parameters for each transformation + pub async fn advanced_batch_transform( + configs: Vec, + ) -> Vec> { + let futures = configs.into_iter().map(|config| { + // Use basic transformation + Self::transform(config.basic_config) + }); + + join_all(futures).await + } + + /// Calculates the quality of a transformation path + pub fn calculate_path_quality( + path: &[Vec], + obstacles: &[Vec], + obstacle_radius: f64, + environment_dimensions: &[f64], + ) -> Result { + if path.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Path cannot be empty for quality calculation".to_string(), + )); + } + + // Calculate path length + let mut path_length = 0.0; + for i in 0..path.len() - 1 { + path_length += euclidean_distance(&path[i], &path[i + 1])?; + } + + // Calculate minimum distance to obstacles + let mut min_obstacle_distance = f64::MAX; + if !obstacles.is_empty() { + for point in path { + for obstacle in obstacles { + let distance = euclidean_distance(point, obstacle)?; + if distance < min_obstacle_distance { + min_obstacle_distance = distance; + } + } + } + } else { + min_obstacle_distance = f64::MAX; // No obstacles, so distance is "infinite" + } + + // Calculate path smoothness (using angle between segments) + let mut smoothness_penalty = 0.0; + if path.len() >= 3 { + for i in 0..path.len() - 2 { + let v1 = vector_subtract(&path[i + 1], &path[i])?; + let v2 = vector_subtract(&path[i + 2], &path[i + 1])?; + + let dot_product = vector_dot_product(&v1, &v2)?; + let v1_len = vector_length(&v1)?; + let v2_len = vector_length(&v2)?; + + if v1_len > 0.0 && v2_len > 0.0 { + let cos_angle = dot_product / (v1_len * v2_len); + let angle = cos_angle.acos(); + + // Penalty increases with sharper turns + smoothness_penalty += angle; + } + } + } + + // Obstacle proximity penalty (increases as we get closer to obstacle_radius) + let obstacle_penalty = if min_obstacle_distance < obstacle_radius * 2.0 { + (obstacle_radius * 2.0 - min_obstacle_distance) / obstacle_radius + } else { + 0.0 + }; + + // Environment utilization factor - how well the path uses available space + let env_size = environment_dimensions.iter().product::(); + let path_box_size = calculate_path_box_size(path)?; + let utilization_factor = path_box_size / env_size; + + // Combine factors into an overall quality score (higher is better) + // We normalize the path length by dividing by the number of segments + let normalized_path_length = path_length / (path.len() - 1) as f64; + let normalized_smoothness = smoothness_penalty / (path.len() - 2).max(1) as f64; + + // Quality formula: We want shorter paths, smoother paths, paths that avoid obstacles, and good use of space + let quality = + 10.0 - normalized_path_length - normalized_smoothness * 2.0 - obstacle_penalty * 5.0 + + utilization_factor * 2.0; + + Ok(quality.max(0.0)) // Ensure quality is non-negative + } +} + +/// Calculates the box size that encompasses the entire path +fn calculate_path_box_size(path: &[Vec]) -> Result { + if path.is_empty() { + return Err(BellandeArchError::InvalidCoordinates( + "Path cannot be empty for box size calculation".to_string(), + )); + } + + let dimensions = path[0].len(); + + // Find min and max for each dimension + let mut min_coords = vec![f64::MAX; dimensions]; + let mut max_coords = vec![f64::MIN; dimensions]; + + for point in path { + if point.len() != dimensions { + return Err(BellandeArchError::DimensionMismatch( + "Not all points in path have the same dimensions".to_string(), + )); + } + + for (i, &value) in point.iter().enumerate() { + min_coords[i] = min_coords[i].min(value); + max_coords[i] = max_coords[i].max(value); + } + } + + // Calculate box size (volume or area) + let mut size = 1.0; + for i in 0..dimensions { + size *= max_coords[i] - min_coords[i]; + } + + Ok(size) +} + +/// Subtracts one vector from another +fn vector_subtract(a: &[f64], b: &[f64]) -> Result, BellandeArchError> { + if a.len() != b.len() { + return Err(BellandeArchError::DimensionMismatch(format!( + "Cannot subtract vectors with different dimensions: {} and {}", + a.len(), + b.len() + ))); + } + + Ok(a.iter().zip(b.iter()).map(|(x, y)| x - y).collect()) +} + +/// Calculates the dot product of two vectors +fn vector_dot_product(a: &[f64], b: &[f64]) -> Result { + if a.len() != b.len() { + return Err(BellandeArchError::DimensionMismatch(format!( + "Cannot calculate dot product of vectors with different dimensions: {} and {}", + a.len(), + b.len() + ))); + } + + Ok(a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()) +} + +/// Calculates the Euclidean length of a vector +fn vector_length(v: &[f64]) -> Result { + Ok(v.iter().map(|x| x * x).sum::().sqrt()) +} + +/// Calculates the Euclidean distance between two points +pub fn euclidean_distance(point1: &[f64], point2: &[f64]) -> Result { + if point1.len() != point2.len() { + return Err(BellandeArchError::DimensionMismatch(format!( + "Points have different dimensions: {} and {}", + point1.len(), + point2.len() + ))); + } + + let sum_squared: f64 = point1 + .iter() + .zip(point2.iter()) + .map(|(a, b)| (a - b).powi(2)) + .sum(); + + Ok(sum_squared.sqrt()) +} + +/// Validates if a point is within the bounds of a given space +pub fn is_point_valid(point: &[f64], min_bounds: &[f64], max_bounds: &[f64]) -> bool { + if point.len() != min_bounds.len() || point.len() != max_bounds.len() { + return false; + } + + point + .iter() + .zip(min_bounds.iter().zip(max_bounds.iter())) + .all(|(p, (min, max))| p >= min && p <= max) +} diff --git a/src/algorithm/bellande_node_importance.rs b/src/algorithm/bellande_node_importance.rs index 3441dd2..479846e 100644 --- a/src/algorithm/bellande_node_importance.rs +++ b/src/algorithm/bellande_node_importance.rs @@ -12,3 +12,6 @@ // 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 bellande_step::make_bellande_node_importance_request; diff --git a/src/algorithm/bellande_particle.rs b/src/algorithm/bellande_particle.rs index 3441dd2..0e284d1 100644 --- a/src/algorithm/bellande_particle.rs +++ b/src/algorithm/bellande_particle.rs @@ -12,3 +12,6 @@ // 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 bellande_step::make_bellande_particle_request; diff --git a/src/algorithm/bellande_probability.rs b/src/algorithm/bellande_probability.rs index 3441dd2..5d33f8b 100644 --- a/src/algorithm/bellande_probability.rs +++ b/src/algorithm/bellande_probability.rs @@ -12,3 +12,6 @@ // 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 bellande_step::make_bellande_probability_request; diff --git a/src/algorithm/bellande_segment.rs b/src/algorithm/bellande_segment.rs index 3441dd2..70bfc24 100644 --- a/src/algorithm/bellande_segment.rs +++ b/src/algorithm/bellande_segment.rs @@ -12,3 +12,6 @@ // 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 bellande_step::make_bellande_segment_request; diff --git a/src/algorithm/bellande_tree.rs b/src/algorithm/bellande_tree.rs index 3441dd2..4d61199 100644 --- a/src/algorithm/bellande_tree.rs +++ b/src/algorithm/bellande_tree.rs @@ -12,3 +12,6 @@ // 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 bellande_step::make_bellande_tree_request; diff --git a/src/mesh/architecture.rs b/src/mesh/architecture.rs new file mode 100644 index 0000000..4f41d85 --- /dev/null +++ b/src/mesh/architecture.rs @@ -0,0 +1,22 @@ +// Copyright (C) 2025 Bellande Architecture Mechanism Research Innovation Center, Ronaldson Bellande + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::algorithm::bellande_limit; +use crate::algorithm::bellande_node_importance; +use crate::algorithm::bellande_particle; +use crate::algorithm::bellande_probability; +use crate::algorithm::bellande_segment; +use crate::algorithm::bellande_step; +use crate::algorithm::bellande_tree; diff --git a/src/mesh/connections.rs b/src/mesh/connections.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/mesh/mod.rs b/src/mesh/mod.rs index 9dcb920..80d2012 100644 --- a/src/mesh/mod.rs +++ b/src/mesh/mod.rs @@ -1,2 +1,2 @@ -pub mod connections; +pub mod architecture; pub mod mesh;