neat/pruning

Population-pruning orchestration for the NEAT controller.

Pruning is the controller's "remove structure on purpose" chapter. It answers two different questions that are easy to blur together when you only look at the public Neat methods:

The root file keeps those two controller-facing entrypoints together because callers usually decide between them at the orchestration level, not at the metric-math level. The lower-level policy resolution and population math live in core/, while facade/ keeps the stable lazy Neat wrappers that call into this file.

Read this chapter when you want to understand when pruning is triggered and which controller state changes as a result. Read core/ when you need the detailed schedule, baseline, or metric-adjustment rules. Read facade/ when you need the public async wrapper behavior on Neat itself.

flowchart TD
  Evolve["evolve() generation step"] --> Schedule{"Scheduled pruning active?"}
  Schedule -->|yes| Evolution["applyEvolutionPruning()\ncompute target sparsity for this generation"]
  Schedule -->|no| SkipSchedule["leave scheduled pruning idle"]
  Evolution --> Population["prune compatible genomes in place"]
  Metrics["population metrics\n(nodes or connections)"] --> Adaptive{"Adaptive pruning enabled?"}
  Adaptive -->|yes| AdaptiveApply["applyAdaptivePruning()\nupdate shared prune level if drift exceeds tolerance"]
  Adaptive -->|no| SkipAdaptive["keep adaptive controller idle"]
  AdaptiveApply --> State["update _adaptivePruneLevel\nand baseline when needed"]
  AdaptiveApply --> Population

neat/pruning/pruning.ts

applyAdaptivePruning

applyAdaptivePruning(): void

Run the adaptive pruning controller.

Use this path when pruning should follow population behavior rather than a fixed generation calendar. Adaptive pruning watches a chosen population-level metric, such as mean node count or mean connection count, and nudges one shared prune level toward the configured sparsity target.

Unlike applyEvolutionPruning(), this controller maintains state across calls. The first adaptive pass captures a baseline metric, later passes compare the current metric against the target remaining value, and only when the drift exceeds the tolerance does the controller update _adaptivePruneLevel and reapply that shared level across the population.

This makes adaptive pruning the maintenance-oriented counterpart to the schedule-driven evolve hook: scheduled pruning answers "is this a pruning generation?" while adaptive pruning answers "has the population drifted far enough from the desired complexity level that the prune level should change?"

Returns: Nothing. The baseline and shared prune level may be created or updated, and compatible genomes may then be pruned in place.

Example:

host.options.adaptivePruning = {
  enabled: true,
  metric: 'connections',
  targetSparsity: 0.4,
  tolerance: 0.05,
  adjustRate: 0.02,
};

applyAdaptivePruning.call(host);
// The host may initialize its baseline, update _adaptivePruneLevel,
// and prune compatible genomes if the metric drift is large enough.

applyEvolutionPruning

applyEvolutionPruning(): void

Apply evolution-time pruning to the current population.

Use this path when pruning should follow the generation schedule rather than a live feedback loop. The evolve orchestration calls into this entrypoint to ask a simple controller question: "for this generation, should pruning run, and if so, how sparse should genomes become right now?"

This function does not maintain long-lived adaptive controller state. Instead it reads the configured evolutionPruning schedule, checks whether the current generation is inside the active window, computes the ramped target sparsity for that moment in the run, and forwards the result to the genomes. The host generation counter is read but not mutated here.

Choose this entrypoint when you want pruning to be predictable and tied to generation timing. Use applyAdaptivePruning() instead when pruning should react to observed population size or complexity metrics at runtime.

Returns: Nothing. Compatible genomes may be pruned in place when the schedule is active, but no adaptive controller fields are updated.

Example:

host.generation = 40;
host.options.evolutionPruning = {
  startGeneration: 20,
  interval: 5,
  rampGenerations: 10,
  targetSparsity: 0.3,
};

applyEvolutionPruning.call(host);
// Compatible genomes prune themselves using the schedule-derived sparsity.
Generated from source JSDoc • GitHub