fugue-evo 0.2.0

Evolutionary computation for Rust: classical EC algorithms plus evolutionary inference - evolutionary algorithms as probabilistic programs (tempered SMC in trace space, built on fugue-ppl)
Documentation
# Design Philosophy

Fugue-evo is built on several core design principles that distinguish it from traditional GA libraries.

## Evolution as Bayesian Inference

The central insight of fugue-evo is that evolutionary algorithms can be understood through a probabilistic lens:

### Fitness as Likelihood

In Bayesian terms:
- **Prior**: Initial population distribution
- **Likelihood**: Fitness function (how well does this solution explain our objective?)
- **Posterior**: Population after selection

Selection acts as **conditioning**: we observe that solutions should have high fitness, and update our population distribution accordingly.

### Learnable Operators

Traditional GAs use fixed operators with hand-tuned parameters. Fugue-evo treats operator parameters as **uncertain quantities** that can be learned:

```rust,ignore
// Traditional: fixed mutation rate
let mutation_rate = 0.1;

// Fugue-evo: learned mutation rate
let mut posterior = BetaPosterior::new(2.0, 2.0);
// ... observe outcomes, update posterior
let learned_rate = posterior.mean();
```

## Type Safety

Rust's type system enables compile-time correctness guarantees.

### Generic Constraints

Operators are typed to their applicable genomes:

```rust,ignore
// SBX only works with real-valued genomes
impl CrossoverOperator<RealVector> for SbxCrossover { ... }

// Order crossover only works with permutations
impl CrossoverOperator<Permutation> for OrderCrossover { ... }
```

### Builder Pattern

Configuration errors are caught at compile time:

```rust,ignore
let ga = SimpleGABuilder::new()
    .population_size(100)
    // Missing required configuration = compile error
    .build()?;  // Only compiles when complete
```

## Trait-Based Abstraction

Core abstractions are defined as traits, enabling extensibility.

### EvolutionaryGenome

Any type can be a genome if it implements:

```rust,ignore
pub trait EvolutionaryGenome: Clone + Send + Sync {
    fn to_trace(&self) -> Trace;
    fn from_trace(trace: &Trace) -> Result<Self, GenomeError>;
}
```

### Operator Traits

Custom operators implement standard traits:

```rust,ignore
pub trait MutationOperator<G>: Send + Sync {
    fn mutate<R: Rng>(&self, genome: &mut G, rng: &mut R);
}
```

## Composability

Components are designed to compose cleanly.

### Operator Composition

```rust,ignore
// Combine mutations
let mutation = CompositeMutation::new(
    GaussianMutation::new(0.1),
    PolynomialMutation::new(20.0),
    0.5, // 50% chance of each
);
```

### Termination Composition

```rust,ignore
// Complex stopping conditions
let term = AnyOf::new(vec![
    Box::new(MaxGenerations::new(1000)),
    Box::new(AllOf::new(vec![
        Box::new(MinGenerations::new(100)),
        Box::new(FitnessStagnation::new(20)),
    ])),
]);
```

## Reproducibility

Scientific use requires reproducibility.

### Seeded RNG

All randomness flows through explicit RNG:

```rust,ignore
let mut rng = StdRng::seed_from_u64(42);
let result = ga.run(&mut rng)?;
// Same seed = same result
```

### Checkpointing

State can be saved and restored:

```rust,ignore
manager.save(&checkpoint)?;
// Later...
let checkpoint = load_checkpoint(&path)?;
```

## Performance

### Optional Parallelism

Parallelism is opt-in and doesn't affect correctness:

```rust,ignore
// Sequential (deterministic)
.parallel(false)

// Parallel (faster, same final result distribution)
.parallel(true)
```

### Efficient Representations

Genomes use efficient storage:

```rust,ignore
// RealVector: contiguous Vec<f64>
// BitString: Vec<bool> (could use bitpacking)
// Permutation: Vec<usize>
```

## Interoperability

### Fugue Integration

Deep integration with probabilistic programming:

```rust,ignore
// Genomes convert to traces
let trace = genome.to_trace();

// Enables trace-based operators
let mutated = trace_mutation(&trace, &mut rng);
```

### WASM Support

Same algorithms run in browser:

```javascript
const optimizer = new SimpleGAOptimizer(config);
const result = optimizer.run(fitnessFunction);
```

## Simplicity

### Avoid Over-Engineering

- Minimal dependencies
- Clear, focused APIs
- Documentation over abstraction

### Progressive Disclosure

Simple cases are simple:

```rust,ignore
// Simplest usage
let result = SimpleGABuilder::new()
    .population_size(100)
    .bounds(bounds)
    .fitness(fitness)
    .max_generations(200)
    .defaults()  // Use sensible defaults
    .run(&mut rng)?;
```

Advanced features are available when needed:

```rust,ignore
// Full control
let result = SimpleGABuilder::new()
    .population_size(100)
    .bounds(bounds)
    .selection(custom_selection)
    .crossover(custom_crossover)
    .mutation(custom_mutation)
    .fitness(custom_fitness)
    .termination(complex_termination)
    .parallel(true)
    .elitism(true)
    .elite_count(5)
    .build()?
    .run(&mut rng)?;
```