gp-lite
Version:
Tiny, zero-dependency GA/GP engine for TypeScript/JS with first-class types, deterministic RNG, and budget-aware runs.
67 lines (48 loc) • 3.48 kB
Markdown
# Architecture Overview
`gp-lite` is a minimal, zero-dependency GA/GP engine designed for clarity, determinism, and extensibility.
## Core Principles
- Deterministic by default: seeded Mulberry32 RNG drives all randomness.
- Pure and synchronous: no async in the core loop.
- Safety-first: robust validation and guarded fitness evaluation.
- Extensible: custom selection (`Selector<T>`), hooks, and problem-specific strategies.
## Key Modules
- `src/types.ts` – Public types and config/result shapes.
- `src/core/engine.ts` – GA/GP loop implementation with selection, breeding, budgets, and metrics.
- `src/core/selection.ts` – Selection operators (e.g., `tournament`).
- `src/lib/rng.ts` – RNG (`mulberry32`).
- `src/lib/format.ts` – `formatResult` helper for human-readable summaries.
- `src/lib/estimate.ts` – Generic time/cost estimator using evaluation counts.
- `src/lib/config.ts` – Shared default normalization (single source of truth).
- `src/lib/errors.ts` – Error types and validation helpers.
- `src/index.ts` – Public export surface.
## Layering & Boundaries
The codebase follows a simple, enforced layering to keep dependencies acyclic and intent clear:
- Core: `src/core/**` implements the evolution engine and selection. It may depend on `src/lib/**` and `src/types.ts`, but must not import from `src/index.ts` or `src/cli.ts`.
- Lib: `src/lib/**` provides pure utilities, config normalization, RNG, errors, and helpers. It must not depend on `src/core/**`.
- Public API: `src/index.ts` re-exports curated types and modules; downstream users should not import deep internals.
- CLI: `src/cli.ts` consumes library APIs but is not imported by any library code.
These boundaries are enforced via ESLint `no-restricted-imports` rules targeting `src/core/**` and `src/lib/**`.
## Data Flow
1. Initialize population via `problem.createRandom` and guarded `fitness`.
2. Rank population, compute stats, and fire generation hooks.
- Unified per-generation payload across sync/async engines: `{ generation, bestFitness, avgFitness, popSize, invalidCount, validShare, bestGenome, elapsedMs, stopReason? }`.
3. Early-stop checks (target, time, evaluations, stall).
4. Breed next generation via selection → crossover/mutation → evaluation.
- Parents are deep-cloned before operator calls; mutation and crossover are
expected to return new genomes. The engine enforces non-aliasing by
cloning results that are returned by reference.
5. Inject immigrants, loop until budget or generation limit.
## Budgets & Concurrency
- `maxEvaluations` – hard cap on total fitness evaluations.
- `maxWallMs`/`timeLimitMs` – wall-clock time budget.
- Engine stops immediately after init if `maxEvaluations` is already exhausted.
Async engine concurrency semantics:
- `concurrency` bounds in-flight `createRandom`/`fitness` work; tasks complete out-of-order, but hooks fire exactly once per generation on the main thread after stats are computed.
- Budgets are enforced between task completions and before scheduling more work.
- `initialPopulation` may seed `GPLiteAsync` without invoking `createRandom` for those individuals.
## Error Handling
- Non-finite or throwing fitness → counted as invalid and mapped to `-Infinity`.
- Optional `isValid` / `repair` hooks for domain-specific validation.
## Selection
- Built-in `tournament(k)` with sampling-with-replacement.
- Users can supply any `Selector<T>` to model different pressures.