Skip to content

Design: KeyForge Evolution

Responsibility: Stochastic optimization (Simulated Annealing). Tier: 1 (The Nucleus)

1. Public API

The crate exposes three entry points for optimization:

Function Purpose
optimize(EngineRequest) Basic optimization with no callback
optimize_with_callback(EngineRequest, CB) Optimization with progress reporting
evolve(engine, config, callback, initial_layout, pinned_keys) Low-level API using pre-compiled engine

2. Progress Callback

Clients receive progress updates via the ProgressCallback trait:

classDiagram
    class ProgressCallback {
        <<trait>>
        +on_progress(epoch, score, layout, ips) OptimizationControl
    }

    class OptimizationControl {
        <<enum>>
        Continue
        Stop
        Abort
    }

    class NoOpCallback

    ProgressCallback <|.. NoOpCallback
    ProgressCallback --> OptimizationControl

The callback returns OptimizationControl to allow graceful stopping or immediate abort.

3. The Annealing Loop

The Optimizer drives the search for the global minimum using configurable strategies.

sequenceDiagram
    participant Opt as Optimizer
    participant State as SearchState
    participant Mut as MutationOperator
    participant Acc as AcceptanceCriteria
    participant Eng as dyn ScoringEngine
    participant CB as ProgressCallback

    Opt->>Eng: score(initial_layout)
    Eng-->>Opt: initial_score
    Opt->>State: new(layout, score, start_temp)

    loop Steps
        Mut->>Eng: calculate_swap_delta(layout, pos_map, a, b)
        Eng-->>Mut: delta (i64)
        Mut-->>Opt: MutationProposal { delta, action }

        alt delta < 0 (Improvement)
            Opt->>State: apply_mutation(action)
            Opt->>State: update_best()
        else delta >= 0 (Degradation)
            Opt->>Acc: should_accept(delta, temp, rng)
            alt Accepted
                Opt->>State: apply_mutation(action)
            end
        end

        Opt->>State: temperature *= cooling_factor

        opt Patience Exceeded
            Opt->>State: reheat_from_best(start_temp, reheat_factor)
        end

        opt Epoch Boundary
            Opt->>CB: on_progress(epoch, score, layout, ips)
            CB-->>Opt: OptimizationControl
        end
    end

    Opt-->>Opt: return best_layout

4. Search State

The SearchState maintains current and best layouts with an optimized position map (pos_map) for O(1) key lookups:

classDiagram
    class SearchState {
        -current_layout: Layout
        -current_score: i64
        -pos_map: Vec~u16~
        -best_layout: Layout
        -best_score: i64
        +temperature: f32
        +new(layout, score, start_temp) Result
        +layout() &Layout
        +pos_map() &[u16]
        +best_layout() &Layout
        +apply_mutation(MutationAction)
        +update_best()
        +reheat_from_best(start_temp, reheat_factor)
    }

    class MutationAction {
        <<enum>>
        Swap(KeyIndex, KeyIndex)
        GroupSwap(KeyIndex, KeyIndex, KeyIndex)
    }

    SearchState --> MutationAction

5. Strategy Traits

The optimizer is parameterized by pluggable strategies:

classDiagram
    class MutationOperator {
        <<trait>>
        +propose(engine, layout, pos_map, rng, temp) Option~MutationProposal~
    }

    class AcceptanceCriteria {
        <<trait>>
        +should_accept(delta, temp, rng) bool
    }

    class TimeKeeper {
        <<trait>>
        +now() Instant
        +elapsed(start) Duration
    }

    class GroupMutation {
        +unlocked_indices: Vec~usize~
        +start_temp: f32
        +end_temp: f32
    }

    class CoolingAnnealing

    class RealTimeKeeper

    MutationOperator <|.. GroupMutation
    AcceptanceCriteria <|.. CoolingAnnealing
    TimeKeeper <|.. RealTimeKeeper

Built-in Strategies

Strategy Type Description
GroupMutation Mutation Swaps keys within unlocked indices; scales mutation size with temperature
CoolingAnnealing Acceptance Boltzmann probability: exp(-delta / temp)
RealTimeKeeper Time System clock (replaceable for deterministic tests)

6. Configuration

Annealing parameters come from SearchConfig::Annealing:

Parameter Purpose
steps Total optimization iterations
start_temp Initial temperature
end_temp Final temperature
seed RNG seed for reproducibility
patience Steps without improvement before reheat
reheats Maximum number of reheats
reheat_factor Temperature multiplier on reheat

7. Error Handling

classDiagram
    class EvolutionError {
        <<enum>>
        Physics(PhysicsError)
        Config(String)
        Aborted
    }

8. Module Structure

keyforge-evolution/src/
├── supervisor/
│   ├── strategies/
│   │   ├── annealing.rs   # CoolingAnnealing
│   │   ├── group.rs       # GroupMutation
│   │   └── scratch.rs     # Experimental strategies
│   ├── annealing.rs       # Optimizer, AnnealingConfig
│   ├── optimizer.rs       # evolve(), optimize() entry points
│   ├── state.rs           # SearchState
│   └── traits.rs          # MutationOperator, AcceptanceCriteria, TimeKeeper
├── errors.rs              # EvolutionError
└── lib.rs                 # Public exports, ProgressCallback