Skip to main content

hill_descent_lib/
lib.rs

1//! # hill_descent_lib
2//!
3//! A genetic algorithm library for n-dimensional optimization problems.
4//!
5//! This library implements a spatial genetic algorithm that divides the search space into
6//! adaptive regions, allowing efficient exploration of complex fitness landscapes. It's
7//! particularly well-suited for optimization problems where gradient information is unavailable
8//! or unreliable.
9//!
10//! ## Quick Start
11//!
12//! ```rust
13//! use hill_descent_lib::{GlobalConstants, SingleValuedFunction, setup_world};
14//! use std::ops::RangeInclusive;
15//!
16//! // Define your fitness function (lower is better)
17//! #[derive(Debug)]
18//! struct Quadratic;
19//!
20//! impl SingleValuedFunction for Quadratic {
21//!     fn single_run(&self, params: &[f64]) -> f64 {
22//!         // Minimize: x² + y²
23//!         params[0].powi(2) + params[1].powi(2)
24//!     }
25//! }
26//!
27//! // Set up and run optimization
28//! use hill_descent_lib::TrainingData;
29//! let bounds = vec![-10.0..=10.0, -10.0..=10.0];
30//! let constants = GlobalConstants::new(100, 10);
31//! let mut world = setup_world(&bounds, constants, Box::new(Quadratic));
32//!
33//! for _ in 0..100 {
34//!     world.training_run(TrainingData::None { floor_value: 0.0 });
35//! }
36//!
37//! println!("Best score: {}", world.get_best_score());
38//! ```
39//!
40//! ## Core Concepts
41//!
42//! - **World**: The main optimization container that manages the population and search space
43//! - **Organisms**: Individual solutions with genetic material (DNA) that evolve over generations
44//! - **Regions**: Spatial partitions of the search space that adapt based on organism distribution
45//! - **Fitness Function**: User-defined function to minimize (implement [`SingleValuedFunction`])
46//!
47//! ## Features
48//!
49//! - N-dimensional optimization (tested with 100+ dimensions)
50//! - Adaptive spatial regions for efficient exploration
51//! - Deterministic results via seeded RNG
52//! - Parallel processing with Rayon
53//! - Optional tracing support (feature: `enable-tracing`)
54//!
55//! ## Algorithm Overview
56//!
57//! 1. **Initialization**: Random population within specified bounds
58//! 2. **Evaluation**: Each organism scored by fitness function
59//! 3. **Regional Competition**: Organisms compete within their spatial regions
60//! 4. **Reproduction**: Better organisms get more offspring via sexual reproduction
61//! 5. **Mutation**: Genetic variation through adaptive mutation
62//! 6. **Adaptation**: Regions dynamically adjust based on population distribution
63//!
64//! The algorithm automatically manages carrying capacity, organism aging, mutation rates,
65//! and search space expansion.
66
67// Use mimalloc as the global allocator for better performance on Windows
68// Temporarily disabled for testing
69// use mimalloc::MiMalloc;
70
71// #[global_allocator]
72// static GLOBAL: MiMalloc = MiMalloc;
73
74// Internal modules - not exposed in public API
75mod gamete;
76mod gen_hybrid_range;
77mod locus;
78mod phenotype;
79
80// Public modules containing public types and traits
81pub mod parameters;
82pub mod training_data;
83pub mod world;
84
85#[cfg(test)]
86pub mod test_utils;
87
88#[cfg(feature = "enable-tracing")]
89pub mod tracing_init;
90
91#[cfg(feature = "enable-tracing")]
92pub use tracing_init::init as init_tracing;
93
94// Re-export log macros for unified logging interface when tracing enabled
95#[cfg(feature = "enable-tracing")]
96pub use log::{debug, error, info, trace, warn};
97
98// When logging is disabled, provide no-op macros
99#[cfg(not(feature = "enable-tracing"))]
100#[macro_export]
101macro_rules! trace {
102    ($($arg:tt)*) => {{}};
103}
104
105#[cfg(not(feature = "enable-tracing"))]
106#[macro_export]
107macro_rules! debug {
108    ($($arg:tt)*) => {{}};
109}
110
111#[cfg(not(feature = "enable-tracing"))]
112#[macro_export]
113macro_rules! info {
114    ($($arg:tt)*) => {{}};
115}
116
117#[cfg(not(feature = "enable-tracing"))]
118#[macro_export]
119macro_rules! warn {
120    ($($arg:tt)*) => {{}};
121}
122
123#[cfg(not(feature = "enable-tracing"))]
124#[macro_export]
125macro_rules! error {
126    ($($arg:tt)*) => {{}};
127}
128
129// Internal constants used by the algorithm implementation
130pub(crate) const NUM_SYSTEM_PARAMETERS: usize = 7;
131pub(crate) const E0: f64 = f64::MIN_POSITIVE;
132
133use std::ops::RangeInclusive;
134
135// Re-export core public types for convenient imports
136pub use parameters::GlobalConstants;
137pub use training_data::TrainingData;
138pub use world::World;
139pub use world::format_score;
140pub use world::single_valued_function::SingleValuedFunction;
141pub use world::world_function::WorldFunction;
142
143/// Creates and initializes a new optimization world.
144///
145/// This is the primary entry point for setting up an optimization problem. It creates
146/// a [`World`] instance with a random initial population distributed according to the
147/// specified parameter bounds.
148///
149/// # Arguments
150///
151/// * `params` - Slice of `RangeInclusive<f64>` defining the bounds for each dimension.
152///   The length of this slice determines the dimensionality of the problem. Each range
153///   specifies the minimum and maximum allowed values for that parameter.
154///
155/// * `global_constants` - Configuration for the genetic algorithm including population
156///   size, number of regions, and random seed. See [`GlobalConstants`] for details.
157///
158/// * `function` - Boxed trait object implementing [`WorldFunction`]. This defines the
159///   fitness function to optimize. Typically you implement [`SingleValuedFunction`] which
160///   automatically provides the [`WorldFunction`] implementation.
161///
162/// # Returns
163///
164/// A fully initialized [`World`] ready to begin optimization via [`World::training_run`].
165///
166/// # Examples
167///
168/// Basic 2D optimization:
169///
170/// ```rust
171/// use hill_descent_lib::{GlobalConstants, SingleValuedFunction, setup_world};
172/// use std::ops::RangeInclusive;
173///
174/// #[derive(Debug)]
175/// struct Sphere;
176///
177/// impl SingleValuedFunction for Sphere {
178///     fn single_run(&self, params: &[f64]) -> f64 {
179///         params.iter().map(|x| x * x).sum()
180///     }
181/// }
182///
183/// let bounds = vec![-5.0..=5.0, -5.0..=5.0];
184/// let constants = GlobalConstants::new(100, 10);
185/// let world = setup_world(&bounds, constants, Box::new(Sphere));
186/// ```
187///
188/// Higher dimensional problem:
189///
190/// ```rust
191/// use hill_descent_lib::{GlobalConstants, SingleValuedFunction, setup_world};
192/// use std::ops::RangeInclusive;
193/// # #[derive(Debug)]
194/// # struct Sphere;
195/// # impl SingleValuedFunction for Sphere {
196/// #     fn single_run(&self, params: &[f64]) -> f64 {
197/// #         params.iter().map(|x| x * x).sum()
198/// #     }
199/// # }
200///
201/// // Optimize in 50 dimensions
202/// let bounds = vec![-10.0..=10.0; 50];
203/// let constants = GlobalConstants::new(500, 25);
204/// let world = setup_world(&bounds, constants, Box::new(Sphere));
205/// ```
206///
207/// # Panics
208///
209/// Panics if `global_constants` has a population size of zero or target regions of zero.
210/// See [`GlobalConstants::new`] for details.
211pub fn setup_world(
212    params: &[RangeInclusive<f64>],
213    global_constants: GlobalConstants,
214    function: Box<dyn WorldFunction>,
215) -> World {
216    World::new(params, global_constants, function)
217}