AutoEmulate's emulators are all regressors, which is the wrong shape for an output that
spends most of its range pinned at a single value.
Our case is a sympathetic-neuron model. Spike frequency is exactly 0 below rheobase
and jumps to one spike per window above it — there is no value in between that the
simulator can produce. Across a 2048-point Sobol design, 36 of 84 outputs were zero in
a median 82% of samples. A smooth regressor splits the difference: it predicts
fractions of a spike across the silent region and undershoots above it. On a synthetic
version of that cliff, an MLP reaches R² 0.18 and averages +4.1 where the truth
is 0.
This is a bifurcation rather than noise — the simulator changes behaviour as a parameter
crosses a threshold — and a stationary kernel has to choose one length scale for both
sides of it. The literature's answer is two stages: a classifier for where the boundary
is, and a regressor for the magnitude fitted only on the far side, so the floor cannot
drag it down.
We've implemented this in circulatory_autogen
as TwoPhaseEmulator, keeping AutoEmulate for both regression halves and using
sklearn only for the classifier — the part AutoEmulate doesn't cover:
👉 physiomelinks/circulatory_autogen#486
Same synthetic cliff, MLP either way, 300 training points:
| output |
plain |
two-phase |
| cliff (spike-count-like) |
0.177 |
0.723 |
| smooth |
0.856 |
0.856 |
It returns exactly 0 on 47/47 floor rows, and leaves outputs with no floor untouched —
the gate applies only where a floor actually exists.
Detection is data-driven rather than domain-specific: an output "has a floor" when one
value accounts for ≥25% of the design. A resting potential pinned at −85 mV is handled
the same way as a spike count pinned at 0.
Would AutoEmulate be interested in this upstream? It looks generally useful — any
simulator with a threshold, a regime change, or a saturating output has the same shape,
and it composes with the existing emulators rather than replacing them (in our version
the variants are exposed as two_phase_<name> for each registered emulator, and are
deliberately excluded from all since two stages cost more to fit than one).
Happy to open a PR if the design fits, or to discuss where it should sit. Related to but
distinct from #1031.
AutoEmulate's emulators are all regressors, which is the wrong shape for an output that
spends most of its range pinned at a single value.
Our case is a sympathetic-neuron model. Spike frequency is exactly 0 below rheobase
and jumps to one spike per window above it — there is no value in between that the
simulator can produce. Across a 2048-point Sobol design, 36 of 84 outputs were zero in
a median 82% of samples. A smooth regressor splits the difference: it predicts
fractions of a spike across the silent region and undershoots above it. On a synthetic
version of that cliff, an
MLPreaches R² 0.18 and averages +4.1 where the truthis 0.
This is a bifurcation rather than noise — the simulator changes behaviour as a parameter
crosses a threshold — and a stationary kernel has to choose one length scale for both
sides of it. The literature's answer is two stages: a classifier for where the boundary
is, and a regressor for the magnitude fitted only on the far side, so the floor cannot
drag it down.
We've implemented this in circulatory_autogen
as
TwoPhaseEmulator, keeping AutoEmulate for both regression halves and usingsklearn only for the classifier — the part AutoEmulate doesn't cover:
👉 physiomelinks/circulatory_autogen#486
Same synthetic cliff,
MLPeither way, 300 training points:It returns exactly 0 on 47/47 floor rows, and leaves outputs with no floor untouched —
the gate applies only where a floor actually exists.
Detection is data-driven rather than domain-specific: an output "has a floor" when one
value accounts for ≥25% of the design. A resting potential pinned at −85 mV is handled
the same way as a spike count pinned at 0.
Would AutoEmulate be interested in this upstream? It looks generally useful — any
simulator with a threshold, a regime change, or a saturating output has the same shape,
and it composes with the existing emulators rather than replacing them (in our version
the variants are exposed as
two_phase_<name>for each registered emulator, and aredeliberately excluded from
allsince two stages cost more to fit than one).Happy to open a PR if the design fits, or to discuss where it should sit. Related to but
distinct from #1031.