tweann_nif_fallback (faber_tweann v2.4.0)

View Source

Pure Erlang fallback implementations for TWEANN NIFs.

This module provides pure Erlang implementations of all NIF functions. These are used when the Rust NIF is not loaded or unavailable. Performance will be slower than NIF but functionality is preserved.

Copyright 2025 R.G. Lefever Licensed under Apache-2.0

Summary

Functions

Benchmark evaluate (returns microseconds per evaluation).

Build cumulative fitness array for roulette wheel.

One forward step for the whole population (pure-Erlang path). Uses this module's evaluate_cfc formula per hidden neuron, for consistency with the per-neuron fallback path in network_evaluator.

Compile a population of CfC feedforward nets (pure-Erlang path). Returns an opaque term (not a NIF resource); cfc_pop_step/3 consumes it.

Compile network to internal format (Erlang record).

Compute reward component with normalization.

Batch compute weighted reward.

Batch dot product.

Dot product with pre-flattened arrays.

Euclidean distance between two vectors.

Batch euclidean distance, sorted by distance.

Evaluate network with inputs, starting from zeroed memory.

Batch evaluate network.

Evaluate CfC (closed-form continuous-time) neuron.

Batch CfC evaluation for time series.

Evaluate ODE-based LTC neuron.

Evaluate, threading the memory organelles' state.

Compute fitness statistics in single pass.

Flatten nested weight structure.

K-nearest neighbor novelty score.

Batch KNN novelty for entire population.

Mutate weights with Gaussian perturbation. Each weight has MutationRate chance of mutation. Mutated weights are either perturbed (PerturbRate) or randomized.

Batch mutate with per-genome parameters. Each tuple: {Weights, MutationRate, PerturbRate, PerturbStrength}

Batch mutate with uniform parameters across all genomes.

Mutate weights with specific random seed for reproducibility.

Generate random weights in range [-1.0, 1.0].

Batch generate random weights. Each tuple: {Count, Mean, StdDev}

Generate random weights from Gaussian distribution.

Generate random weights with specific seed.

Roulette wheel selection with binary search.

Shannon entropy of a distribution.

Batch compute distances between target and multiple weight vectors. DistanceType: l1 | l2

L1 (Manhattan) distance between two weight vectors.

L2 (Euclidean) distance between two weight vectors.

Weighted moving average with exponential decay.

Z-score normalization.

Functions

benchmark_evaluate(Network, Inputs, Iterations)

-spec benchmark_evaluate(map(), [float()], pos_integer()) -> float().

Benchmark evaluate (returns microseconds per evaluation).

build_cumulative_fitness(Fitnesses)

-spec build_cumulative_fitness([float()]) -> {[float()], float()}.

Build cumulative fitness array for roulette wheel.

cfc_pop_step(_, StatesPop, InputsPop)

-spec cfc_pop_step(term(), [[float()]], [[float()]]) -> {[[float()]], [[float()]]}.

One forward step for the whole population (pure-Erlang path). Uses this module's evaluate_cfc formula per hidden neuron, for consistency with the per-neuron fallback path in network_evaluator.

compatibility_distance(ConnectionsA, ConnectionsB, C1, C2, C3)

-spec compatibility_distance(list(), list(), float(), float(), float()) -> float().

Compute NEAT compatibility distance.

compile_cfc_pop(WeightsPop, TausPop, InputSize, HiddenSize, OutputSize, Bound)

-spec compile_cfc_pop([[float()]], [[float()]], pos_integer(), pos_integer(), pos_integer(), float()) ->
                         term().

Compile a population of CfC feedforward nets (pure-Erlang path). Returns an opaque term (not a NIF resource); cfc_pop_step/3 consumes it.

compile_network(Nodes, InputCount, OutputIndices)

-spec compile_network(list(), non_neg_integer(), [non_neg_integer()]) -> map().

Compile network to internal format (Erlang record).

compute_reward_component(History, Current)

-spec compute_reward_component([float()], float()) -> {float(), float(), float()}.

Compute reward component with normalization.

compute_weighted_reward(Components)

-spec compute_weighted_reward([{[float()], float(), float()}]) -> float().

Batch compute weighted reward.

dot_product_batch(Batch)

-spec dot_product_batch([{[float()], [float()], float()}]) -> [float()].

Batch dot product.

dot_product_flat(Signals, Weights, Bias)

-spec dot_product_flat([float()], [float()], float()) -> float().

Flat dot product.

dot_product_preflattened(SignalsFlat, WeightsFlat, Bias)

-spec dot_product_preflattened([float()], [float()], float()) -> float().

Dot product with pre-flattened arrays.

euclidean_distance(V1, V2)

-spec euclidean_distance([float()], [float()]) -> float().

Euclidean distance between two vectors.

euclidean_distance_batch(Target, Others)

-spec euclidean_distance_batch([float()], [[float()]]) -> [{non_neg_integer(), float()}].

Batch euclidean distance, sorted by distance.

evaluate(Network, Inputs)

-spec evaluate(map(), [float()]) -> [float()].

Evaluate network with inputs, starting from zeroed memory.

evaluate_batch(Network, InputsList)

-spec evaluate_batch(map(), [[float()]]) -> [[float()]].

Batch evaluate network.

evaluate_cfc(Input, State, Tau, Bound)

-spec evaluate_cfc(float(), float(), float(), float()) -> {float(), float()}.

Evaluate CfC (closed-form continuous-time) neuron.

⚠ THIS DISCARDED TAU. It bound the argument as _Tau and never read it, so a liquid TIME-CONSTANT neuron's time constant did nothing on this path, and it also used a different backbone and returned tanh(state) rather than the state. Against the native implementation on the same inputs it diverged by up to 0.36, which is not rounding.

That matters beyond this function. network_evaluator:evaluate_with_state/2 routes CfC through tweann_nif:evaluate_cfc/4, so a CfC network computed a different function depending on whether the native library had loaded, and nothing said so. An adversarial review of the research corpus found the same seam there: the CfC-bearing insights from 024 onward ran on this bridge and their raw feeds record no implementation, so which dynamics produced them had to be inferred from the data rather than read off the record.

The native implementation is now the reference and this mirrors it exactly: f = input / max(tau, 0.001), h = tanh(input), gate = sigmoid(-f), state clamped to the bound, and the OUTPUT IS THE STATE.

evaluate_cfc_batch(Inputs, InitialState, Tau, Bound)

-spec evaluate_cfc_batch([float()], float(), float(), float()) -> [{float(), float()}].

Batch CfC evaluation for time series.

evaluate_cfc_with_weights(Input, State, Tau, Bound, BackboneWeights, HeadWeights)

-spec evaluate_cfc_with_weights(float(), float(), float(), float(), [float()], [float()]) ->
                                   {float(), float()}.

Evaluate CfC with custom weights.

evaluate_ode(Input, State, Tau, Bound, Dt)

-spec evaluate_ode(float(), float(), float(), float(), float()) -> {float(), float()}.

Evaluate ODE-based LTC neuron.

evaluate_ode_with_weights(Input, State, Tau, Bound, Dt, BackboneWeights, HeadWeights)

-spec evaluate_ode_with_weights(float(), float(), float(), float(), float(), [float()], [float()]) ->
                                   {float(), float()}.

Evaluate ODE with custom weights.

evaluate_with_state(_, Inputs, State)

-spec evaluate_with_state(map(), [float()], [float()]) -> {[float()], [float()]}.

Evaluate, threading the memory organelles' state.

⚠ THIS MUST MATCH native evaluate_with_state EXACTLY, pass for pass. Three passes, and the order is the whole mechanism:

1. EMIT. Every delay publishes the value it captured last tick. This is why a delay's output does not depend on this tick's inputs, and why a feedback path through one is not a cycle. 2. FORWARD. Ordinary nodes evaluate in list order, which the caller has guaranteed is topological. 3. CAPTURE. Every delay stores the weighted sum of its inputs, which may name nodes that come after it.

An empty state reads as zeros. A state of the wrong length returns two empty lists rather than being padded.

fitness_stats(Fitnesses)

-spec fitness_stats([float()]) -> {float(), float(), float(), float(), float(), float()}.

Compute fitness statistics in single pass.

flatten_weights(WeightedInputs)

-spec flatten_weights([{term(), [{float(), float(), float(), list()}]}]) ->
                         {[float()], [non_neg_integer()]}.

Flatten nested weight structure.

histogram(Values, NumBins, MinVal, MaxVal)

-spec histogram([float()], pos_integer(), float(), float()) -> [non_neg_integer()].

Histogram binning.

knn_novelty(Target, Population, Archive, K)

-spec knn_novelty([float()], [[float()]], [[float()]], pos_integer()) -> float().

K-nearest neighbor novelty score.

knn_novelty_batch(Behaviors, Archive, K)

-spec knn_novelty_batch([[float()]], [[float()]], pos_integer()) -> [float()].

Batch KNN novelty for entire population.

mutate_weights(Weights, MutationRate, PerturbRate, PerturbStrength)

-spec mutate_weights([float()], float(), float(), float()) -> [float()].

Mutate weights with Gaussian perturbation. Each weight has MutationRate chance of mutation. Mutated weights are either perturbed (PerturbRate) or randomized.

mutate_weights_batch(Batch)

-spec mutate_weights_batch([{[float()], float(), float(), float()}]) -> [[float()]].

Batch mutate with per-genome parameters. Each tuple: {Weights, MutationRate, PerturbRate, PerturbStrength}

mutate_weights_batch_uniform(WeightsList, MutationRate, PerturbRate, PerturbStrength)

-spec mutate_weights_batch_uniform([[float()]], float(), float(), float()) -> [[float()]].

Batch mutate with uniform parameters across all genomes.

mutate_weights_seeded(Weights, MutationRate, PerturbRate, PerturbStrength, Seed)

-spec mutate_weights_seeded([float()], float(), float(), float(), integer()) -> [float()].

Mutate weights with specific random seed for reproducibility.

random_weights(Count)

-spec random_weights(non_neg_integer()) -> [float()].

Generate random weights in range [-1.0, 1.0].

random_weights_batch(Batch)

-spec random_weights_batch([{non_neg_integer(), float(), float()}]) -> [[float()]].

Batch generate random weights. Each tuple: {Count, Mean, StdDev}

random_weights_gaussian(Count, Mean, StdDev)

-spec random_weights_gaussian(non_neg_integer(), float(), float()) -> [float()].

Generate random weights from Gaussian distribution.

random_weights_seeded(Count, Seed)

-spec random_weights_seeded(non_neg_integer(), integer()) -> [float()].

Generate random weights with specific seed.

roulette_select(Cumulative, Total, RandomVal)

-spec roulette_select([float()], float(), float()) -> non_neg_integer().

Roulette wheel selection with binary search.

roulette_select_batch(Cumulative, Total, RandomVals)

-spec roulette_select_batch([float()], float(), [float()]) -> [non_neg_integer()].

Batch roulette selection.

shannon_entropy(Values)

-spec shannon_entropy([float()]) -> float().

Shannon entropy of a distribution.

tournament_select(Contestants, Fitnesses)

-spec tournament_select([non_neg_integer()], [float()]) -> non_neg_integer().

Tournament selection.

weight_distance_batch(Target, Others, DistanceType)

-spec weight_distance_batch([float()], [[float()]], l1 | l2) -> [float()].

Batch compute distances between target and multiple weight vectors. DistanceType: l1 | l2

weight_distance_l1(W1, W2)

-spec weight_distance_l1([float()], [float()]) -> float().

L1 (Manhattan) distance between two weight vectors.

weight_distance_l2(W1, W2)

-spec weight_distance_l2([float()], [float()]) -> float().

L2 (Euclidean) distance between two weight vectors.

weighted_moving_average(Values, Decay)

-spec weighted_moving_average([float()], float()) -> float().

Weighted moving average with exponential decay.

z_score(Value, Mean, StdDev)

-spec z_score(float(), float(), float()) -> float().

Z-score normalization.