neat/multiobjective/fronts

Frontier construction for NEAT multi-objective ranking.

This chapter picks up where dominance/ stops. Once the pairwise pass has recorded how many times each genome is dominated and which genomes each row blocks, this file peels the population into ordered Pareto fronts and writes the _moRank annotations that later selection and telemetry reads expect.

The boundary is intentionally narrow:

That split matters because front construction depends on one stable contract: matrix row order, population order, and dominance-state indices must all keep referring to the same genome while fronts are peeled layer by layer. This file therefore works only with index lists and one shared population array; it does not rebuild the matrix or recompute pairwise dominance.

Conceptually, the pass answers three questions:

  1. Which indices already belong to the first front?
  2. When a front is removed, which domination counts should drop to zero next?
  3. Which genomes should be annotated with the current Pareto rank before the next frontier is collected?
flowchart TD
  A[Dominance state with firstFrontIndices] --> B[Take current front indices]
  B --> C[Annotate genomes with current _moRank]
  C --> D[Relax domination counts for dominated neighbors]
  D --> E[Collect indices whose counts reach zero]
  E --> F[Append current front to ordered results]
  F --> G{Guard exceeded?}
  G -->|No| B
  G -->|Yes| H[Stop with bounded result]

Read this chapter when the missing question is "how did the bookkeeping from the dominance pass become ordered Pareto fronts?" Read dominance/ first if the missing context is how firstFrontIndices or domination counts were computed in the first place.

neat/multiobjective/fronts/multiobjective.fronts.ts

annotateGenomeRank

annotateGenomeRank(
  population: default[],
  genomeIndex: number,
  frontRank: number,
): void

Annotates a genome with its Pareto front rank.

Ranking is stored directly on the genome so later selection, telemetry, and archive helpers can read one stable annotation instead of carrying a parallel rank table beside the population.

Parameters:

appendFront

appendFront(
  paretoFronts: default[][],
  population: default[],
  currentFrontIndices: number[],
): void

Appends the current front (index list) as genome references to the paretoFronts accumulator.

The accumulator stores genomes, not indices, because the returned fronts are meant to be consumed by later crowding and archive code. The conversion from stable indices to genome references happens only after the current layer has been fully identified.

Parameters:

buildNextFrontIndices

buildNextFrontIndices(
  population: default[],
  dominanceState: DominanceState,
  currentFrontIndices: number[],
  currentFrontRank: number,
): number[]

Builds the next front by applying rank annotations and dominance updates.

This helper is the inner loop of frontier peeling: mark every genome in the current front with the same rank, then remove each genome's blocking influence so newly non-dominated neighbors can surface as the next front.

Parameters:

Returns: Indices for the next front.

buildParetoFronts

buildParetoFronts(
  population: default[],
  dominanceState: DominanceState,
  maxFrontRankGuard: number,
): default[][]

Builds Pareto fronts from a precomputed dominance state.

This performs the “peeling” phase of fast non-dominated sorting:

The implementation is intentionally breadth-first. It never revisits pairwise comparisons; instead it trusts the dominance-state handoff and repeatedly relaxes domination counts until the next layer becomes visible.

Side effects:

Guard:

Parameters:

Returns: Ordered Pareto fronts (rank order).

collectNextFrontIndices

collectNextFrontIndices(
  dominanceState: DominanceState,
  genomeIndex: number,
  nextFrontIndices: number[],
): void

Collects indices that become non-dominated after removing the current genome’s dominance influence.

Every dominated neighbor starts this pass with one known blocker: the genome that just joined the current front. Decrementing its domination count models removing that blocker. When the count reaches zero, the neighbor has no remaining dominating opponents and can join the next frontier.

Parameters:

incrementFrontRank

incrementFrontRank(
  currentFrontRank: number,
): number

Increments the front rank counter.

Keeping rank advancement in its own helper makes the frontier loop easier to read and keeps the guard check phrased in terms of the next rank value.

Parameters:

Returns: Incremented front rank.

MAX_PARETO_FRONT_RANK_GUARD

Maximum number of Pareto fronts to allow during ranking before aborting.

This is a defensive guard against pathological conditions (e.g., corrupted dominance bookkeeping) that could otherwise cause long/infinite loops. A healthy ranking pass should terminate well before this threshold, so the constant exists as a safety rail rather than a normal control knob.

shouldStopFrontRanking

shouldStopFrontRanking(
  currentFrontRank: number,
  maxFrontRankGuard: number,
): boolean

Determines whether ranking should stop due to a safety guard.

The guard is intentionally blunt: if frontier peeling advances beyond the configured maximum rank, the function stops and returns the fronts collected so far rather than risking a pathological or corrupted infinite loop.

Parameters:

Returns: true if ranking should stop.

Generated from source JSDoc • GitHub