Skip to content

Commit 7d58c15

Browse files
Switch CTRNN integration from forward Euler to exponential Euler (ETD1)
The exponential Euler method integrates the linear decay term -u/tau exactly, making it unconditionally stable regardless of dt/tau ratio. Forward Euler required dt < 2*tau for stability. The nonlinear forcing term is still held constant per step (same assumption as before). Update formula: u_new = exp(-dt/tau) * u_old + (1 - exp(-dt/tau)) * z Test reference values regenerated for the new integrator. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 04a7371 commit 7d58c15

File tree

2 files changed

+10
-5
lines changed

2 files changed

+10
-5
lines changed

neat/ctrnn/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Handles the continuous-time recurrent neural network implementation."""
22

3+
import math
34

45
from neat.graphs import required_for_output
56

@@ -78,7 +79,8 @@ def advance(self, inputs, advance_time, time_step=None):
7879
node_inputs = [ivalues[i] * w for i, w in ne.links]
7980
s = ne.aggregation(node_inputs)
8081
z = ne.activation(ne.bias + ne.response * s)
81-
ovalues[node_key] += dt / ne.time_constant * (-ovalues[node_key] + z)
82+
decay = math.exp(-dt / ne.time_constant)
83+
ovalues[node_key] = decay * ovalues[node_key] + (1.0 - decay) * z
8284

8385
self.time_seconds += dt
8486

tests/test_ctrnn.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,15 @@ def test_basic_two_neuron_dynamics():
4848

4949
# Check specific reference values at selected timesteps to guard against
5050
# regressions in the CTRNN integration behavior.
51+
# These values use exponential Euler (ETD1) integration, which integrates
52+
# the linear decay term exactly: u(t+h) = decay*u(t) + (1-decay)*z
53+
# where decay = exp(-h/tau).
5154
reference = {
5255
0: (0.0, 0.0),
53-
100: (0.3746852284, 0.8115273872),
54-
500: (0.5634208426, 0.1952743492),
55-
1149: (0.4092483263, 0.8024978195),
56-
1249: (0.7531862678, 0.3112381247),
56+
100: (0.3087387628, 0.7906422519),
57+
500: (0.7965631789, 0.4505240611),
58+
1149: (0.4952237403, 0.1974657233),
59+
1249: (0.2539079366, 0.7016150055),
5760
}
5861
tol = 1e-6
5962
for idx, (exp0, exp1) in reference.items():

0 commit comments

Comments
 (0)