546 lines
15 KiB
Rust
546 lines
15 KiB
Rust
//! Comprehensive benchmarks for temporal-attractor-studio crate
|
|
//!
|
|
//! Benchmarks cover:
|
|
//! - Phase space embedding (target: <20ms)
|
|
//! - Lyapunov exponent calculation (target: <500ms)
|
|
//! - Attractor detection (target: <100ms)
|
|
//! - Trajectory analysis
|
|
//! - Dimension estimation
|
|
//! - Chaos detection
|
|
//!
|
|
//! Performance targets:
|
|
//! - Phase space: <20ms for n=1000
|
|
//! - Lyapunov: <500ms
|
|
//! - Detection: <100ms
|
|
|
|
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput};
|
|
use midstreamer_attractor::{
|
|
AttractorStudio, PhaseSpace, Trajectory, AttractorType,
|
|
lyapunov::calculate_lyapunov_exponent,
|
|
embedding::reconstruct_phase_space,
|
|
detection::detect_attractor_type,
|
|
dimension::estimate_correlation_dimension,
|
|
};
|
|
|
|
// ============================================================================
|
|
// Test Signal Generators
|
|
// ============================================================================
|
|
|
|
fn generate_lorenz_attractor(n: usize) -> Vec<(f64, f64, f64)> {
|
|
let dt = 0.01;
|
|
let sigma = 10.0;
|
|
let rho = 28.0;
|
|
let beta = 8.0 / 3.0;
|
|
|
|
let mut points = Vec::with_capacity(n);
|
|
let (mut x, mut y, mut z) = (1.0, 1.0, 1.0);
|
|
|
|
for _ in 0..n {
|
|
let dx = sigma * (y - x);
|
|
let dy = x * (rho - z) - y;
|
|
let dz = x * y - beta * z;
|
|
|
|
x += dx * dt;
|
|
y += dy * dt;
|
|
z += dz * dt;
|
|
|
|
points.push((x, y, z));
|
|
}
|
|
|
|
points
|
|
}
|
|
|
|
fn generate_rossler_attractor(n: usize) -> Vec<(f64, f64, f64)> {
|
|
let dt = 0.01;
|
|
let a = 0.2;
|
|
let b = 0.2;
|
|
let c = 5.7;
|
|
|
|
let mut points = Vec::with_capacity(n);
|
|
let (mut x, mut y, mut z) = (1.0, 1.0, 1.0);
|
|
|
|
for _ in 0..n {
|
|
let dx = -y - z;
|
|
let dy = x + a * y;
|
|
let dz = b + z * (x - c);
|
|
|
|
x += dx * dt;
|
|
y += dy * dt;
|
|
z += dz * dt;
|
|
|
|
points.push((x, y, z));
|
|
}
|
|
|
|
points
|
|
}
|
|
|
|
fn generate_henon_map(n: usize) -> Vec<(f64, f64)> {
|
|
let a = 1.4;
|
|
let b = 0.3;
|
|
|
|
let mut points = Vec::with_capacity(n);
|
|
let (mut x, mut y) = (0.1, 0.1);
|
|
|
|
for _ in 0..n {
|
|
let x_new = 1.0 - a * x * x + y;
|
|
let y_new = b * x;
|
|
|
|
points.push((x, y));
|
|
x = x_new;
|
|
y = y_new;
|
|
}
|
|
|
|
points
|
|
}
|
|
|
|
fn generate_time_series(n: usize, pattern: &str) -> Vec<f64> {
|
|
match pattern {
|
|
"sine" => (0..n).map(|i| (i as f64 * 0.1).sin()).collect(),
|
|
"chaotic" => {
|
|
let lorenz = generate_lorenz_attractor(n);
|
|
lorenz.iter().map(|(x, _, _)| *x).collect()
|
|
}
|
|
"random" => (0..n).map(|i| {
|
|
((i as f64 * 7919.0).sin() * 10000.0) % 100.0
|
|
}).collect(),
|
|
"periodic" => (0..n).map(|i| {
|
|
(i as f64 * 0.1).sin() + 0.5 * (i as f64 * 0.3).sin()
|
|
}).collect(),
|
|
_ => vec![0.0; n],
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Phase Space Embedding Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_phase_space_embedding(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("phase_space_embedding");
|
|
|
|
for size in [100, 500, 1000, 5000].iter() {
|
|
group.throughput(Throughput::Elements(*size as u64));
|
|
|
|
// Dimension 2
|
|
group.bench_with_input(
|
|
BenchmarkId::new("dim2", size),
|
|
size,
|
|
|b, &n| {
|
|
let data = generate_time_series(n, "chaotic");
|
|
b.iter(|| {
|
|
black_box(reconstruct_phase_space(
|
|
black_box(&data),
|
|
black_box(2),
|
|
black_box(1)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
|
|
// Dimension 3
|
|
group.bench_with_input(
|
|
BenchmarkId::new("dim3", size),
|
|
size,
|
|
|b, &n| {
|
|
let data = generate_time_series(n, "chaotic");
|
|
b.iter(|| {
|
|
black_box(reconstruct_phase_space(
|
|
black_box(&data),
|
|
black_box(3),
|
|
black_box(1)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
|
|
// Dimension 5
|
|
group.bench_with_input(
|
|
BenchmarkId::new("dim5", size),
|
|
size,
|
|
|b, &n| {
|
|
let data = generate_time_series(n, "chaotic");
|
|
b.iter(|| {
|
|
black_box(reconstruct_phase_space(
|
|
black_box(&data),
|
|
black_box(5),
|
|
black_box(1)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_embedding_delays(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("embedding_delays");
|
|
|
|
let data = generate_time_series(1000, "chaotic");
|
|
|
|
for delay in [1, 5, 10, 20, 50].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("delay", delay),
|
|
delay,
|
|
|b, &d| {
|
|
b.iter(|| {
|
|
black_box(reconstruct_phase_space(
|
|
black_box(&data),
|
|
black_box(3),
|
|
black_box(d)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Lyapunov Exponent Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_lyapunov_calculation(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("lyapunov_exponent");
|
|
|
|
// Different attractor types
|
|
group.bench_function("lorenz", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
let trajectory: Vec<f64> = points.iter().map(|(x, _, _)| *x).collect();
|
|
|
|
b.iter(|| {
|
|
black_box(calculate_lyapunov_exponent(
|
|
black_box(&trajectory),
|
|
black_box(3),
|
|
black_box(10)
|
|
))
|
|
});
|
|
});
|
|
|
|
group.bench_function("rossler", |b| {
|
|
let points = generate_rossler_attractor(1000);
|
|
let trajectory: Vec<f64> = points.iter().map(|(x, _, _)| *x).collect();
|
|
|
|
b.iter(|| {
|
|
black_box(calculate_lyapunov_exponent(
|
|
black_box(&trajectory),
|
|
black_box(3),
|
|
black_box(10)
|
|
))
|
|
});
|
|
});
|
|
|
|
group.bench_function("periodic", |b| {
|
|
let trajectory = generate_time_series(1000, "periodic");
|
|
|
|
b.iter(|| {
|
|
black_box(calculate_lyapunov_exponent(
|
|
black_box(&trajectory),
|
|
black_box(3),
|
|
black_box(10)
|
|
))
|
|
});
|
|
});
|
|
|
|
// Varying data sizes
|
|
for size in [500, 1000, 2000, 5000].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("size", size),
|
|
size,
|
|
|b, &n| {
|
|
let trajectory = generate_time_series(n, "chaotic");
|
|
b.iter(|| {
|
|
black_box(calculate_lyapunov_exponent(
|
|
black_box(&trajectory),
|
|
black_box(3),
|
|
black_box(10)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Attractor Detection Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_attractor_detection(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("attractor_detection");
|
|
|
|
// Known attractors
|
|
group.bench_function("lorenz_detection", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
|
|
b.iter(|| {
|
|
black_box(detect_attractor_type(black_box(&points)))
|
|
});
|
|
});
|
|
|
|
group.bench_function("rossler_detection", |b| {
|
|
let points = generate_rossler_attractor(1000);
|
|
|
|
b.iter(|| {
|
|
black_box(detect_attractor_type(black_box(&points)))
|
|
});
|
|
});
|
|
|
|
// Different data sizes
|
|
for size in [100, 500, 1000, 2000].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("size", size),
|
|
size,
|
|
|b, &n| {
|
|
let points = generate_lorenz_attractor(n);
|
|
b.iter(|| {
|
|
black_box(detect_attractor_type(black_box(&points)))
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Trajectory Analysis Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_trajectory_analysis(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("trajectory_analysis");
|
|
|
|
// Trajectory reconstruction
|
|
group.bench_function("reconstruction", |b| {
|
|
let data = generate_time_series(1000, "chaotic");
|
|
|
|
b.iter(|| {
|
|
let phase_space = reconstruct_phase_space(&data, 3, 10);
|
|
black_box(Trajectory::from_phase_space(black_box(phase_space)))
|
|
});
|
|
});
|
|
|
|
// Distance calculations
|
|
group.bench_function("distances", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
let trajectory = Trajectory::from_points(points);
|
|
|
|
b.iter(|| {
|
|
black_box(trajectory.calculate_distances())
|
|
});
|
|
});
|
|
|
|
// Nearest neighbors
|
|
group.bench_function("nearest_neighbors", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
let trajectory = Trajectory::from_points(points);
|
|
|
|
b.iter(|| {
|
|
black_box(trajectory.find_nearest_neighbors(black_box(10)))
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Dimension Estimation Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_dimension_estimation(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("dimension_estimation");
|
|
|
|
// Correlation dimension
|
|
group.bench_function("correlation_dim_lorenz", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
|
|
b.iter(|| {
|
|
black_box(estimate_correlation_dimension(
|
|
black_box(&points),
|
|
black_box(20)
|
|
))
|
|
});
|
|
});
|
|
|
|
group.bench_function("correlation_dim_henon", |b| {
|
|
let points = generate_henon_map(1000);
|
|
let points_3d: Vec<(f64, f64, f64)> = points
|
|
.iter()
|
|
.map(|(x, y)| (*x, *y, 0.0))
|
|
.collect();
|
|
|
|
b.iter(|| {
|
|
black_box(estimate_correlation_dimension(
|
|
black_box(&points_3d),
|
|
black_box(20)
|
|
))
|
|
});
|
|
});
|
|
|
|
// Varying sample sizes
|
|
for size in [500, 1000, 2000].iter() {
|
|
group.bench_with_input(
|
|
BenchmarkId::new("size", size),
|
|
size,
|
|
|b, &n| {
|
|
let points = generate_lorenz_attractor(n);
|
|
b.iter(|| {
|
|
black_box(estimate_correlation_dimension(
|
|
black_box(&points),
|
|
black_box(20)
|
|
))
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Chaos Detection Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_chaos_detection(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("chaos_detection");
|
|
|
|
// Chaotic signals
|
|
group.bench_function("chaotic_lorenz", |b| {
|
|
let points = generate_lorenz_attractor(1000);
|
|
let signal: Vec<f64> = points.iter().map(|(x, _, _)| *x).collect();
|
|
|
|
b.iter(|| {
|
|
let lyapunov = calculate_lyapunov_exponent(&signal, 3, 10);
|
|
black_box(lyapunov > 0.0)
|
|
});
|
|
});
|
|
|
|
// Periodic signals
|
|
group.bench_function("periodic", |b| {
|
|
let signal = generate_time_series(1000, "periodic");
|
|
|
|
b.iter(|| {
|
|
let lyapunov = calculate_lyapunov_exponent(&signal, 3, 10);
|
|
black_box(lyapunov > 0.0)
|
|
});
|
|
});
|
|
|
|
// Random signals
|
|
group.bench_function("random", |b| {
|
|
let signal = generate_time_series(1000, "random");
|
|
|
|
b.iter(|| {
|
|
let lyapunov = calculate_lyapunov_exponent(&signal, 3, 10);
|
|
black_box(lyapunov > 0.0)
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Complete Pipeline Benchmarks
|
|
// ============================================================================
|
|
|
|
fn bench_complete_analysis(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("complete_analysis");
|
|
|
|
group.bench_function("full_pipeline", |b| {
|
|
let data = generate_time_series(1000, "chaotic");
|
|
|
|
b.iter(|| {
|
|
// Phase space reconstruction
|
|
let phase_space = reconstruct_phase_space(&data, 3, 10);
|
|
|
|
// Convert to 3D points for analysis
|
|
let points: Vec<(f64, f64, f64)> = phase_space
|
|
.iter()
|
|
.map(|p| (p[0], p[1], p[2]))
|
|
.collect();
|
|
|
|
// Attractor detection
|
|
let attractor_type = detect_attractor_type(&points);
|
|
|
|
// Lyapunov calculation
|
|
let lyapunov = calculate_lyapunov_exponent(&data, 3, 10);
|
|
|
|
// Dimension estimation
|
|
let dimension = estimate_correlation_dimension(&points, 20);
|
|
|
|
black_box((attractor_type, lyapunov, dimension))
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Criterion Configuration
|
|
// ============================================================================
|
|
|
|
criterion_group! {
|
|
name = embedding_benches;
|
|
config = Criterion::default()
|
|
.sample_size(100)
|
|
.measurement_time(std::time::Duration::from_secs(10))
|
|
.warm_up_time(std::time::Duration::from_secs(3));
|
|
targets = bench_phase_space_embedding, bench_embedding_delays
|
|
}
|
|
|
|
criterion_group! {
|
|
name = lyapunov_benches;
|
|
config = Criterion::default()
|
|
.sample_size(50)
|
|
.measurement_time(std::time::Duration::from_secs(15));
|
|
targets = bench_lyapunov_calculation
|
|
}
|
|
|
|
criterion_group! {
|
|
name = detection_benches;
|
|
config = Criterion::default()
|
|
.sample_size(100)
|
|
.measurement_time(std::time::Duration::from_secs(10));
|
|
targets = bench_attractor_detection
|
|
}
|
|
|
|
criterion_group! {
|
|
name = trajectory_benches;
|
|
config = Criterion::default()
|
|
.sample_size(100);
|
|
targets = bench_trajectory_analysis
|
|
}
|
|
|
|
criterion_group! {
|
|
name = dimension_benches;
|
|
config = Criterion::default()
|
|
.sample_size(50)
|
|
.measurement_time(std::time::Duration::from_secs(12));
|
|
targets = bench_dimension_estimation
|
|
}
|
|
|
|
criterion_group! {
|
|
name = chaos_benches;
|
|
config = Criterion::default()
|
|
.sample_size(50);
|
|
targets = bench_chaos_detection
|
|
}
|
|
|
|
criterion_group! {
|
|
name = pipeline_benches;
|
|
config = Criterion::default()
|
|
.sample_size(30)
|
|
.measurement_time(std::time::Duration::from_secs(15));
|
|
targets = bench_complete_analysis
|
|
}
|
|
|
|
criterion_main!(
|
|
embedding_benches,
|
|
lyapunov_benches,
|
|
detection_benches,
|
|
trajectory_benches,
|
|
dimension_benches,
|
|
chaos_benches,
|
|
pipeline_benches
|
|
);
|