Skip to content

Two-phase (classify → regress) emulation for simulators with discontinuous outputs #1043

Description

@FinbarArgus

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions