use thiserror::Error;
pub mod ftle;
pub mod echo_state;
pub mod attractor;
pub mod time_expansion_bridge;
pub use ftle::*;
#[derive(Error, Debug)]
pub enum TemporalStudioError {
#[error("FTLE calculation error: {0}")]
Ftle(String),
#[error("VP-tree construction error: {0}")]
VpTree(String),
#[error("Delay embedding error: {0}")]
Embedding(String),
#[error("Data processing error: {0}")]
DataProcessing(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Anyhow error: {0}")]
Anyhow(#[from] anyhow::Error),
}
pub type StudioResult<T> = Result<T, TemporalStudioError>;
pub mod prelude {
pub use crate::{
TemporalStudioError, StudioResult,
VpTree, FtleParams, LyapunovResult,
estimate_lyapunov, estimate_lyapunov_default, estimate_lyapunov_with_params,
delay_embed, mean, dist, theiler_exclude,
calculate_ftle_segment, calculate_ftle_field,
};
pub use anyhow::Result;
}
pub fn init() -> StudioResult<()> {
#[cfg(feature = "tracing")]
{
let _ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.try_init();
tracing::info!("Temporal Attractor Studio initialized");
}
#[cfg(not(feature = "tracing"))]
{
println!("Temporal Attractor Studio initialized");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ftle_calculation() {
let trajectory = vec![
vec![1.0, 2.0],
vec![1.1, 2.1],
vec![1.2, 2.2],
vec![1.3, 2.3],
vec![1.4, 2.4],
vec![1.5, 2.5],
vec![1.6, 2.6],
vec![1.7, 2.7],
vec![1.8, 2.8],
vec![1.9, 2.9],
vec![2.0, 3.0],
vec![2.1, 3.1],
vec![2.2, 3.2],
vec![2.3, 3.3],
vec![2.4, 3.4],
];
let result = estimate_lyapunov_default(&trajectory);
match result {
Ok(lyap_result) => {
assert!(lyap_result.lambda.is_finite());
assert!(lyap_result.doubling_time > 0.0);
assert!(lyap_result.lyapunov_time > 0.0);
assert!(lyap_result.pairs_found > 0);
}
Err(e) => {
println!("Expected error for simple linear data: {}", e);
assert!(true);
}
}
}
#[test]
fn test_delay_embedding() {
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let embedded = delay_embed(&series, 3, 1).unwrap();
assert_eq!(embedded.len(), 4);
assert_eq!(embedded[0], vec![1.0, 2.0, 3.0]);
assert_eq!(embedded[1], vec![2.0, 3.0, 4.0]);
assert_eq!(embedded[2], vec![3.0, 4.0, 5.0]);
assert_eq!(embedded[3], vec![4.0, 5.0, 6.0]);
}
#[test]
fn test_ftle_params() {
let params = FtleParams::default();
assert_eq!(params.dt, 0.01);
assert_eq!(params.k_fit, 12);
assert_eq!(params.theiler, 20);
assert_eq!(params.max_pairs, 4000);
assert_eq!(params.min_init_sep, 1e-12);
}
}