Hybrid Systems¶
This page covers the public API of flowcean.hybrid and its subpackages:
- Modeling, Simulation, and Plotting: system definitions, automaton diagrams, trajectories, explicit sampling, plotting, and the opt-in
plot_style()context for custom Matplotlib figures. - Benchmarks: reusable system factories.
- HyDRA identification:
HyDRALearnerreturns fully assignedLearnedFlowsor raisesHyDRAIdentificationErrorwith the failingsegment. Useto_labeled_framesfor selector training orto_flow_idsfor NumPy assignments. Progress observers useHyDRACallbackorPlotCallback; selector helpers are also available fromflowcean.hybrid.hydra.selector. - Dainarx identification:
fit_dainarx_dynamicsdiscovers sampled NARX dynamics;fit_dainarx_transitionslearns guards and post-switch predictors.fit_dainarxcomposes both stages. See the Dainarx example. - FaMoS-DTW identification: independent partial location discovery, sampled affine ARX fitting, and source-conditioned next-location trees. See the FaMoS example.
- MMSR identification: coupled scalar symbolic regression and separate soft-membership transition modeling, with inspectable mappings and guards. See the MMSR example.
See the hybrid systems guide for modeling concepts and the evaluation guide for selecting observations and scoring predictions with existing tools.
Modeling, Simulation, and Plotting
¶
Hybrid-system modeling, automaton diagrams, simulation, and traces.
Attributes¶
Classes¶
CrossingDirection
¶
DelayFunction
¶
Bases: Protocol
Transition delay sampled at each accepted detection, not at execution.
The transition's scheduling policy determines which detections are accepted and which sampled occurrence can execute. Each sample freezes that occurrence's deadline at detection time plus the returned duration. Return a finite, nonnegative scalar duration in model time units. The source location's state, effective parameters, and residence time are supplied at detection. See FlowFunction for shared inputs, supported signatures, and purity requirements.
EventSurface
dataclass
¶
EventSurface(fn: Callable[..., float], direction: CrossingDirection = EITHER, label: str | None = None)
Scalar event surface defining a simulated transition event.
Supply a continuous scalar function whose zero crossings identify the
switching boundary. RISING accepts negative-to-positive crossings,
FALLING positive-to-negative crossings, and EITHER both. Exact zero
on entry follows the transition's
SurfaceEntryPolicy. NaN values raise
InvalidEventSurfaceValueError;
nonzero values, including infinities, retain their sign in entry checks.
Continuous crossing detection brackets roots using integration-step
endpoint signs. Multiple crossings within a step can be missed; tangency
detection depends on the step endpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., float]
|
Root function. See FlowFunction for callback inputs. |
required |
direction
|
CrossingDirection
|
Crossing direction. Defaults to either direction. |
EITHER
|
label
|
str | None
|
Optional display label. |
None
|
Attributes¶
direction
class-attribute
instance-attribute
¶
direction: CrossingDirection = CrossingDirection.EITHER
EventSurfaceFunction
¶
Bases: Protocol
Scalar event-surface callback.
See FlowFunction for shared inputs and supported callback signatures.
Flow
dataclass
¶
Reusable continuous-state derivative law.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Derivative]
|
Function returning the state derivative. See FlowFunction for callback inputs. Scalar derivative returns are accepted only for single-state systems, both during solver evaluation and when derivatives are explicitly sampled from a trajectory. |
required |
label
|
str | None
|
Optional display label. |
None
|
FlowFunction
¶
Bases: Protocol
Continuous-state derivative callback.
Flow, event-surface, reset, and delay callbacks share these inputs:
physical time (t), the continuous state vector, effective
parameters, an input_stream returning a vector for a requested
time, and the current visit's elapsed location_time.
Model callbacks may declare any subset using these names, including
keyword-only arguments. Named callbacks with **kwargs receive all five
inputs. Four-positional callbacks receive (t, state, parameters,
input_stream); positional-only arguments, *args, and noncanonical
required names select that form.
Callbacks and input streams must be pure and deterministic: integration and derivative sampling can evaluate the same inputs repeatedly.
HybridSystem
dataclass
¶
HybridSystem(locations: Sequence[Location], transitions: Sequence[Transition], initial_location: Location, initial_state: State, parameters: Parameters = dict())
Hybrid system with locations and transitions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
locations
|
Sequence[Location]
|
Location objects in this system. |
required |
transitions
|
Sequence[Transition]
|
Transition list defining event surfaces and resets. |
required |
initial_location
|
Location
|
Starting location object. |
required |
initial_state
|
State
|
Initial state vector. |
required |
parameters
|
Parameters
|
Global parameter map passed to callbacks. |
dict()
|
Attributes¶
parameters
class-attribute
instance-attribute
¶
parameters: Parameters = field(default_factory=dict)
Methods:¶
transitions_from
¶
transitions_from(location: Location) -> list[Transition]
Return transitions leaving the given location.
Location
dataclass
¶
Location(flow: Flow, *, label: str | None = None, parameters: Parameters | None = None)
Location(flow: Callable[..., Derivative], *, label: str | None = None, parameters: Parameters | None = None)
Location(flow: Flow | Callable[..., Derivative], *, label: str | None = None, parameters: Parameters | None = None)
Discrete hybrid-automaton location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flow
|
Flow | Callable[..., Derivative]
|
Flow definition or bare derivative callback active here. |
required |
label
|
str | None
|
Optional display label. |
None
|
parameters
|
Parameters | None
|
Location-local parameters overriding system parameters with the same names. Effective maps are snapshotted for each run. |
None
|
Reset
dataclass
¶
State reset applied on a transition.
The callback receives the source location's effective parameters and residence time. Its result must match the continuous-state dimension; a scalar is accepted for a single-state system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., State]
|
Reset function applied at the event time. See FlowFunction for callback inputs. |
required |
label
|
str | None
|
Optional display label. |
None
|
ResetFunction
¶
Bases: Protocol
State reset callback.
See FlowFunction for shared inputs and supported callback signatures.
SurfaceEntryPolicy
¶
Bases: StrEnum
Behavior when a transition surface is exactly zero on location entry.
Entry includes initialization and arrival after a transition. ERROR
raises SurfaceEntryError; TRIGGER
detects the transition immediately (starting its delay); CONTINUE
begins continuous integration with the surface at zero. For CONTINUE,
choose a flow that departs in the direction opposite to the accepted
crossing so integration can advance.
All outgoing surfaces are evaluated before resolving entry. NaN values
are rejected first, then zero ERROR surfaces. Exactly one zero-delay
TRIGGER surface at zero performs a jump; multiple such surfaces raise
AmbiguousTransitionError.
Positive-delay TRIGGER surfaces schedule execution without a jump.
Entry handling repeats at the target after an immediate transition.
Transition
dataclass
¶
Transition(source: Location, target: Location, event_surface: EventSurface | Callable[..., float], reset: Reset | Callable[..., State] | None = None, *, entry_policy: SurfaceEntryPolicy = ERROR, delay: float | Callable[..., float] = 0.0, scheduling_policy: TransitionSchedulingPolicy = FIRST_DETECTION)
Discrete event-triggered transition between locations.
event_surface is a scalar zero-crossing surface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Location
|
Source location. |
required |
target
|
Location
|
Target location. |
required |
event_surface
|
EventSurface | Callable[..., float]
|
Event surface that triggers the transition. |
required |
reset
|
Reset | Callable[..., State] | None
|
Optional reset applied upon transition. |
None
|
entry_policy
|
SurfaceEntryPolicy
|
Behavior when the event surface is exactly zero upon entry to the source location. |
ERROR
|
delay
|
float | Callable[..., float]
|
Fixed finite, nonnegative scalar duration, or a pure, deterministic callback returning one in the model's time units. See DelayFunction for inputs. The callback is evaluated at detection, including entry triggers, and validated then; fixed values are validated on construction. Each accepted detection freezes an occurrence's deadline at detection time plus delay. The source flow and residence clock continue until execution; the reset receives that execution context. Zero (the default) requests immediate execution. |
0.0
|
scheduling_policy
|
TransitionSchedulingPolicy
|
How repeated detections affect this transition's pending occurrence. FIRST_DETECTION (the default) accepts only the first detection for the entire visit, even if it loses to another candidate. LATEST_DETECTION replaces the occurrence on every crossing, advancing or postponing its deadline. EACH_DETECTION samples every crossing and keeps the earliest deadline; equal own deadlines retain the earliest detection time. Every exit, including a self-transition, cancels all occurrences. The earliest outgoing deadline executes. At an exactly equal numerical time, direction-qualified detections are processed before execution; equal earliest deadlines of distinct transitions are ambiguous. No tolerance groups nearby times. |
FIRST_DETECTION
|
Attributes¶
entry_policy
class-attribute
instance-attribute
¶
entry_policy: SurfaceEntryPolicy = SurfaceEntryPolicy.ERROR
scheduling_policy
class-attribute
instance-attribute
¶
scheduling_policy: TransitionSchedulingPolicy = TransitionSchedulingPolicy.FIRST_DETECTION
TransitionSchedulingPolicy
¶
Bases: StrEnum
How detections schedule a transition during one source-location visit.
FIRST_DETECTION accepts only the first detection, even when another
transition currently has an earlier deadline. LATEST_DETECTION accepts
every direction-qualified crossing and replaces this transition's pending
occurrence. EACH_DETECTION samples an independent occurrence at each
crossing; the earliest deadline wins, with equal deadlines retaining the
earliest detection time. Leaving the visit cancels all occurrences.
AmbiguousTransitionError
¶
Bases: HybridSimulationError
Raised for competing entry-time jumps or equal scheduled deadlines.
HybridSimulationError
¶
Bases: RuntimeError
Base class for hybrid simulation runtime failures.
InvalidEventSurfaceValueError
¶
InvalidEventSurfaceValueError(transitions: Sequence[Transition], time: float, *, context: str)
SimulationProgressError
¶
Bases: HybridSimulationError
Raised when a solver event does not advance physical time.
SurfaceEntryError
¶
Bases: HybridSimulationError
Raised when an ERROR surface is zero on location entry.
ContinuousSegment
dataclass
¶
ContinuousSegment(location: Location, t_span: tuple[float, float], _solution: Callable[[ndarray], ndarray], _clock: _ResidenceClock, _knots: ndarray)
Continuous evolution in one location over a time interval.
The solver's dense solution and plotting knots are private. The residence clock stays anchored throughout this visit, independent of sampling grids.
Attributes¶
Methods:¶
evaluate
¶
evaluate(time: float) -> TrajectoryPoint
Evaluate within the inclusive interval without invoking callbacks.
The start is after any preceding jump chain; the end is before any following jump. Times must be finite; extrapolation is not allowed.
location_time
¶
Return residence age without evaluating the continuous state.
Event
dataclass
¶
Event(time: float, transition: Transition, state_before: State, state_after: State, microstep: int, location_time_before: float, detection_time: float)
A recorded transition with states immediately before and after it.
Pre- and post-reset state snapshots are detached and read-only.
transition references the model's transition, including its source,
target, event surface, and reset. time is physical simulation time;
microstep orders events at that time, starting at zero.
location_time_before is the age of the source visit before the jump.
detection_time identifies the detection that scheduled the executed
occurrence, possibly on location entry: the first for FIRST_DETECTION,
the latest for LATEST_DETECTION, or the earliest-deadline occurrence for
EACH_DETECTION (earliest detection when its own deadlines tie).
time is when the transition executed after its sampled delay.
They are equal for zero-delay transitions. Unexecuted detections are not
recorded as events.
Attributes¶
HybridTrajectory
dataclass
¶
HybridTrajectory(system: HybridSystem, t_span: tuple[float, float], initial_state: State, initial_location: Location, initial_location_time: float, execution: tuple[ContinuousSegment | Event, ...], _bindings: _RunBindings)
A run's ordered execution, associated model, and parameter snapshots.
Numerical snapshots are read-only. Model identity is retained, but later model parameter edits do not affect this run. Callbacks and input streams must remain deterministic and pure when explicitly resampled.
Attributes¶
events
property
¶
events: tuple[Event, ...]
Individual transitions, including every same-time microstep.
parameters
property
¶
parameters: Mapping[Location, Parameters]
Read-only effective parameter snapshots for all declared locations.
Methods:¶
evaluate
¶
evaluate(time: float) -> TrajectoryPoint
Evaluate a finite time in the inclusive run interval, without callbacks.
At an exact event time, return the final state and location after the entire jump chain, with zero residence age. This includes initial and final events.
sample
¶
sample(times: Iterable[float] | None = None, *, dt: float | None = None, include_state: bool = True, include_location_id: bool = True, include_location_label: bool = False, include_location_time: bool = True, include_inputs: bool = False, include_derivatives: bool = False) -> DataFrame
Build a frame at requested times, using post-transition event values.
At an event time, the row describes the state after the entire same-time chain, as returned by evaluate. Default sampling invokes no callbacks. Inputs and flow derivatives are evaluated at the requested times when included; these callbacks and input streams must remain pure and deterministic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
times
|
Iterable[float] | None
|
Finite, non-descending times within the trajectory's
interval. Accepts generators, duplicates, and an empty grid.
Supply exactly one of |
None
|
dt
|
float | None
|
Positive finite grid spacing. Includes the final endpoint; a zero-duration run produces one row. |
None
|
include_state
|
bool
|
Include continuous-state columns |
True
|
include_location_id
|
bool
|
Include |
True
|
include_location_label
|
bool
|
Include the display |
False
|
include_location_time
|
bool
|
Include elapsed visit time as
|
True
|
include_inputs
|
bool
|
Evaluate the input stream and include |
False
|
include_derivatives
|
bool
|
Evaluate the active flow with the sampled
state and run parameters, including |
False
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
A Polars frame with physical time |
DataFrame
|
Empty grids retain the known state, derivative, and location |
DataFrame
|
schemas. Use Polars to select or rename columns and add row indices. |
Raises:
| Type | Description |
|---|---|
ValueError
|
Grid arguments, input capture, or callback outputs are invalid. |
TrajectoryPoint
dataclass
¶
TrajectoryPoint(state: State, location: Location, location_time: float)
State, location, and residence age at one point in an execution.
The state is a detached, read-only array; location is the active model object.
Functions:¶
build_hybrid_system_dot
¶
build_hybrid_system_dot(system: HybridSystem, *, location_colors: Mapping[Location, str] | None = None, show_event_labels: bool = True, show_reset_labels: bool = True, show_direction: bool = False, show_entry_policy: bool = False) -> str
Export the complete declared automaton as deterministic DOT text.
Locations and transitions retain declaration order, including isolated locations, parallel transitions, and self-loops. A point and incoming arrow mark the initial location. Node IDs are positional, independent of display labels; reordering locations changes their IDs.
Labels follow the model's display-label precedence: explicit labels, then callback names, then positional fallbacks for unnamed objects. Labels are treated as literal text, not DOT markup or mathematical expressions. No simulation or callback evaluation is performed, and Graphviz need not be installed. Positive fixed delays are annotated in model time units; delay callbacks are annotated by name, without evaluation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system
|
HybridSystem
|
Constructed hybrid system to visualize. |
required |
location_colors
|
Mapping[Location, str] | None
|
Optional colors keyed by Location identity, using Matplotlib color names or hex values, as in native plots. Defaults follow this system's declaration order; matching colors across different artifacts do not establish corresponding locations. |
None
|
show_event_labels
|
bool
|
Annotate transitions with event-surface labels. |
True
|
show_reset_labels
|
bool
|
Annotate transitions that have resets with labels. |
True
|
show_direction
|
bool
|
Include the event surface's zero-crossing direction. |
False
|
show_entry_policy
|
bool
|
Include each transition's surface-entry policy. |
False
|
Returns:
| Type | Description |
|---|---|
str
|
DOT source suitable for saving or passing to |
render_dot_svg
¶
Render DOT text as SVG using Graphviz's external dot executable.
No files are written and no viewer is opened. SVG layout can vary between Graphviz versions even when the input DOT is identical.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dot_source
|
str
|
DOT source to render. |
required |
Returns:
| Type | Description |
|---|---|
str
|
SVG text decoded as UTF-8. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Graphviz is not installed or rejects the DOT source. |
plot_locations
¶
plot_locations(trajectory: HybridTrajectory, *, location_colors: Mapping[Location, str] | None = None, show_labels: bool = False, alpha: float = BAND_ALPHA, ax: Axes | None = None) -> Axes
Shade actual positive-duration segments, using Location identity.
plot_state_space
¶
plot_state_space(trajectory: HybridTrajectory, x_dim: int = 0, y_dim: int = 1, *, location_colors: Mapping[Location, str] | None = None, show_location_legend: bool = True, show_event_points: bool = False, ax: Axes | None = None) -> Axes
Plot two selected continuous state coordinates, with time implicit.
Each continuous segment is drawn separately so reset jumps are not joined.
show_event_points optionally marks states on both sides of transitions;
switches without resets are unmarked by default. Returns the axes without
showing, saving, or closing the figure.
plot_style
¶
Temporarily use hybrid plotting defaults for custom Matplotlib figures.
Use with plot_style(): or decorate an application function with
@plot_style(). Defaults cover typography, lines, categorical colors,
legends, backgrounds, and a light grid. Ordinary Matplotlib artist
arguments can override them. Existing artists are not restyled.
Matplotlib rc settings are restored on exit, including exceptional exit.
This context neither selects nor restores a backend and never draws,
shows, saves, or closes figures. As with Matplotlib's rc_context,
settings are process-wide while the explicit context is active. Library
renderers apply their own local styling and do not enter this context.
plot_trajectory
¶
plot_trajectory(trajectory: HybridTrajectory, dims: Sequence[int] | None = None, *, location_colors: Mapping[Location, str] | None = None, show_locations: bool = True, show_location_labels: bool = False, show_events: bool = True, show_event_labels: bool = True, show_event_points: bool = False, ax: Axes | None = None) -> Axes
Plot state coordinates against time without lines across resets.
show_events controls vertical transition indicators and labels;
show_event_points separately marks the states before and after each
transition. Markers are off by default, including at continuous switches.
Returns the axes; the caller owns showing, saving, and closing the figure.
simulate
¶
simulate(system: HybridSystem, t_span: tuple[float, float], x0: Iterable[float] | None = None, location0: Location | None = None, *, input_stream: InputStream | None = None, initial_location_time: float = 0.0, max_jumps: int = 256, rtol: float = 1e-07, atol: float = 1e-09, max_step: float | None = None) -> HybridTrajectory
Integrate a hybrid execution with dense continuous segments.
Sampling is a separate operation on the returned trajectory. Callbacks and input streams must be pure and deterministic under repeated evaluation. Equal endpoints resolve entry transitions without calling the ODE solver. Parameters are snapshotted for every location at the start of the run. Each transition's scheduling policy selects accepted detections and pending occurrences. Delay callbacks run once per accepted detection, freezing that occurrence's deadline. A positive delay keeps the source flow and residence clock active until execution. Leaving the source visit cancels all its occurrences. At exactly equal numerical times, all direction-qualified detections precede execution, even at the final endpoint; a latest detection can postpone execution beyond the run. Distinct transitions with equal earliest deadlines are ambiguous.
Root detection uses integration-step endpoint signs: multiple roots within a step can be missed, and tangencies are step-dependent. Equality means exact numerical equality, not mathematical simultaneity or a tolerance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system
|
HybridSystem
|
Model to simulate. |
required |
t_span
|
tuple[float, float]
|
Finite start and end times, with end at or after start. |
required |
x0
|
Iterable[float] | None
|
Initial continuous state, overriding the model's initial state. |
None
|
location0
|
Location | None
|
Initial location, overriding the model's initial location. |
None
|
input_stream
|
InputStream | None
|
Function returning a one-dimensional input vector at a requested physical time. |
None
|
initial_location_time
|
float
|
Finite, nonnegative age of the initial visit. Each subsequent transition begins a new visit at age zero. Each run starts with no pending transition. |
0.0
|
max_jumps
|
int
|
Maximum number of transitions, including same-time chains. |
256
|
rtol
|
float
|
Relative tolerance for SciPy integration. |
1e-07
|
atol
|
float
|
Absolute tolerance for SciPy integration. |
1e-09
|
max_step
|
float | None
|
Maximum integration step; None uses SciPy's default. |
None
|
Returns:
| Type | Description |
|---|---|
HybridTrajectory
|
The trajectory, retaining its supplied initial condition and ordered |
HybridTrajectory
|
continuous segments and events. |
Raises:
| Type | Description |
|---|---|
HybridSimulationError
|
The transition limit is exceeded or a subclass reports an invalid surface, ambiguous entry, or progress failure. |
ValueError
|
Time bounds, initial conditions, or callback outputs are invalid. |
Benchmarks
¶
Reusable hybrid-system benchmark factories.
Functions:¶
wind_turbine_power
¶
wind_turbine_power(rotor_speeds: Sequence[float] | ndarray, location_labels: Sequence[str], *, parameters: Parameters) -> ndarray
Return generator-shaft mechanical power (W) for each rotor speed.
rotor_speeds are positive angular speeds in rad/s; each corresponding
location_labels entry selects the active torque law. The location
matters in hysteresis bands, where speed alone does not select a law.
parameters are those of the simulated turbine, including its gearbox
ratio. The resulting one-dimensional array contains one power value per
input speed, or no values for empty inputs. This is not electrical output;
rated shaft power is not an instantaneous cap in other modes.
System factories¶
bouncing_ball
¶
bouncing_ball(gravity: float = 9.81, restitution: float = 0.8, initial_state: ndarray | None = None) -> HybridSystem
Create a bouncing ball benchmark system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gravity
|
float
|
Downward acceleration. |
9.81
|
restitution
|
float
|
Velocity multiplier on bounce. |
0.8
|
initial_state
|
ndarray | None
|
Optional initial [height, velocity]. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for a bouncing ball. |
buck_converter
¶
buck_converter(source_voltage: float = 24.0, inductance: float = 0.00265, capacitance: float = 0.0022, load_resistance: float = 10.0, *, inductor_resistance: float = 0.52, switch_resistance: float = 0.2, voltage_high: float = 12.1, voltage_low: float = 11.9, initial_state: Sequence[float] | ndarray | None = None) -> HybridSystem
Create a physical hysteretic nonsynchronous buck converter.
A buck converter is a step-down DC-DC power circuit. A controlled switch connects a constant-voltage supply to an inductor, which feeds a capacitor and resistive load in parallel. The inductor stores magnetic energy and prevents abrupt current changes; the capacitor stores electrical energy and smooths the output voltage. A freewheel diode provides a current path when the switch opens. Nonsynchronous means this diode, rather than a second controlled switch, carries the freewheeling current.
The state is [inductor_current, capacitor_voltage] in amperes and
volts. The capacitor voltage is also the load voltage. Three locations
describe the circuit's conduction states:
switch_on: The source supplies energy through the closed switch. Inductor current typically rises, feeding the load and capacitor.switch_off: The source is disconnected, but inductor current continues through the diode. Stored magnetic energy feeds the output while current falls; the capacitor can still charge during this phase.zero_current: Once inductor current reaches zero, the diode blocks reverse current. Only the capacitor supplies the load, so its voltage decays exponentially with time constantload_resistance * capacitance.
Hysteretic control opens the switch when voltage rises to voltage_high
and closes it when voltage falls to voltage_low. Between thresholds,
the switch retains its state. Switching is state-triggered, not driven by
a fixed-frequency clock. The switch may close before current reaches zero,
skipping zero_current. The thresholds are switching commands, not hard
output-voltage bounds: stored inductor energy can produce overshoot.
The flows follow Kirchhoff's laws and the relations v_L = L * di/dt
and i_C = C * dv/dt. With current i, output voltage v, supply
V_s, inductance L, capacitance C, load resistance R, inductor
resistance r_L, and closed-switch resistance r_S:
- Switch on:
di/dt = (V_s - (r_L + r_S) * i - v) / L. - Switch off:
di/dt = (-r_L * i - v) / L. - In either conducting state:
dv/dt = (i - v / R) / C. - Zero current:
di/dt = 0anddv/dt = -v / (R * C).
Each location has affine continuous dynamics; voltage and current events
select the active circuit configuration. The diode is ideal, with no
forward voltage drop or reverse current. Switching delays and diode
recovery are not modeled; resistive losses are included in r_L,
r_S, and the load.
On entry to zero_current, current is reset exactly to zero to remove
numerical event-localization residuals, not to model a physical impulse.
Lower-threshold voltage residuals within event-time localization precision
are also snapped to the threshold, allowing coincident events to settle
without an artificial integration step.
For a nonzero initial current, the latch starts on below voltage_high
and off at or above it. At zero current, it starts in zero_current
above voltage_low and otherwise starts on. A useful nominal simulation
horizon is 0.02 seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source_voltage
|
float
|
Constant input supply voltage in volts. |
24.0
|
inductance
|
float
|
Inductance in henries. |
0.00265
|
capacitance
|
float
|
Output capacitance in farads. |
0.0022
|
load_resistance
|
float
|
Resistive load in ohms. |
10.0
|
inductor_resistance
|
float
|
Inductor series resistance in ohms. |
0.52
|
switch_resistance
|
float
|
Closed-switch resistance in ohms. |
0.2
|
voltage_high
|
float
|
Rising output-voltage threshold that switches off. |
12.1
|
voltage_low
|
float
|
Falling output-voltage threshold that switches on. |
11.9
|
initial_state
|
Sequence[float] | ndarray | None
|
Initial |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for hysteretic buck conversion. |
duffing_oscillator
¶
duffing_oscillator(damping_high: float = 0.5, damping_low: float = 0.2, *, linear_stiffness: float = -1.0, cubic_stiffness_high: float = 1.5, cubic_stiffness_low: float = 0.5, inner_radius: float = 0.8, outer_radius: float = 1.2, velocity_retention: float = 0.95, initial_state: ndarray | None = None) -> HybridSystem
Create a normalized unit-mass oscillator with hysteretic switching.
The state has shape (2,) and order [position, velocity]; its
derivative is [velocity, acceleration]. Supply an explicit finite
input of shape (1,), [force], where force is applied force per unit
mass (acceleration). Position and time use normalized units, not a
prescribed SI scale. In location q, the acceleration is
force - damping_q * velocity - linear_stiffness * position
- cubic_stiffness_q * position**3. Both damping and cubic stiffness
change with the location.
A falling crossing of position**2 - inner_radius**2 switches from
high_damping to low_damping. A rising crossing of
position**2 - outer_radius**2 switches back. Both resets preserve
position and multiply velocity by velocity_retention without reversing
it. These are native zero-crossing events, not urgent inequality guards;
exact zero on entry uses the default SurfaceEntryPolicy.ERROR.
Initialization selects low_damping at or inside the inner radius and
high_damping elsewhere, including the hysteresis band. This convention
does not execute a guard. Simulation overrides x0 and location0
retain the simulator's usual entry and crossing semantics.
The default coefficients and boundaries follow Example 1, Figure 2(a) in the published model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
damping_high
|
float
|
High-location damping, in inverse time units. Must be
finite, nonnegative, and at least |
0.5
|
damping_low
|
float
|
Low-location damping, in inverse time units. Must be finite and nonnegative. |
0.2
|
linear_stiffness
|
float
|
Shared signed linear restoring coefficient, in inverse time squared. Must be finite; negative values destabilize the origin while the positive cubic term limits excursions. |
-1.0
|
cubic_stiffness_high
|
float
|
Positive finite high-location cubic restoring coefficient, in inverse position squared per time squared. |
1.5
|
cubic_stiffness_low
|
float
|
Positive finite low-location cubic restoring coefficient, in inverse position squared per time squared. |
0.5
|
inner_radius
|
float
|
Positive finite inward switching radius in position units. |
0.8
|
outer_radius
|
float
|
Finite outward switching radius, larger than the inner radius, in position units. |
1.2
|
velocity_retention
|
float
|
Finite velocity multiplier between zero and one. |
0.95
|
initial_state
|
ndarray | None
|
Optional finite |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem with high- and low-damping locations. |
hybrid_oscillator
¶
hybrid_oscillator(damping_left: float = 0.2, damping_right: float = 0.1, frequency: float = 2.0, initial_state: ndarray | None = None) -> HybridSystem
Create a piecewise damped oscillator benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
damping_left
|
float
|
Damping for the left half-space. |
0.2
|
damping_right
|
float
|
Damping for the right half-space. |
0.1
|
frequency
|
float
|
Oscillation frequency. |
2.0
|
initial_state
|
ndarray | None
|
Optional initial [position, velocity]. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for a hybrid oscillator. |
impact_oscillator
¶
impact_oscillator(damping: float = 0.1, stiffness: float = 4.0, *, restitution: float = 0.7, initial_state: ndarray | None = None) -> HybridSystem
Create an impact oscillator driven by an external force.
Simulate with an explicit finite one-element input vector [force].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
damping
|
float
|
Linear damping coefficient. |
0.1
|
stiffness
|
float
|
Spring stiffness. |
4.0
|
restitution
|
float
|
Velocity multiplier on impact. |
0.7
|
initial_state
|
ndarray | None
|
Optional initial [position, velocity]. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for an impact oscillator. |
location_cycle
¶
location_cycle(location_count: int = 4, dimension: int = 4, dwell_time: float = 0.5, initial_state: ndarray | None = None) -> HybridSystem
Create a scalable hybrid system that cycles through locations.
The system has location_count locations, each with linear dynamics active for
dwell_time. Transitions leave the continuous state unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
location_count
|
int
|
Number of locations in the cycle. |
4
|
dimension
|
int
|
Dimension of the continuous state. |
4
|
dwell_time
|
float
|
Time to stay in each location. |
0.5
|
initial_state
|
ndarray | None
|
Optional initial state (length dimension). |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem cycling through multiple linear locations. |
pid_controlled_plant
¶
pid_controlled_plant(kp: float = 6.0, ki: float = 2.0, kd: float = 1.0, stiffness: float = 3.0, damping: float = 0.6, *, u_min: float = -1.0, u_max: float = 1.0, initial_state: ndarray | None = None) -> HybridSystem
Create a PID-controlled second-order plant with saturation.
The state is [position, velocity, integral_error]. Simulate with an
explicit finite two-element input vector [reference, reference_rate];
the second component is the reference's time derivative. The controller
tracks this reference and saturates the control input, yielding hybrid
locations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kp
|
float
|
Proportional gain. |
6.0
|
ki
|
float
|
Integral gain. |
2.0
|
kd
|
float
|
Derivative gain. |
1.0
|
stiffness
|
float
|
Plant stiffness. |
3.0
|
damping
|
float
|
Plant damping. |
0.6
|
u_min
|
float
|
Minimum control input. |
-1.0
|
u_max
|
float
|
Maximum control input. |
1.0
|
initial_state
|
ndarray | None
|
Optional initial state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for PID control with saturation. |
piecewise_affine
¶
piecewise_affine(a_left: ndarray | None = None, a_right: ndarray | None = None, b_left: ndarray | None = None, b_right: ndarray | None = None, threshold: float = 0.0, initial_state: ndarray | None = None) -> HybridSystem
Create a piecewise affine system with oscillatory switching.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a_left
|
ndarray | None
|
Linear dynamics matrix for the left region. |
None
|
a_right
|
ndarray | None
|
Linear dynamics matrix for the right region. |
None
|
b_left
|
ndarray | None
|
Affine offset for the left region. |
None
|
b_right
|
ndarray | None
|
Affine offset for the right region. |
None
|
threshold
|
float
|
Event-surface threshold on x[0]. |
0.0
|
initial_state
|
ndarray | None
|
Optional initial state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured with piecewise affine dynamics. |
relay_integrator
¶
relay_integrator(rate: float = 1.0, lower: float = -1.0, upper: float = 1.0, initial_state: ndarray | None = None) -> HybridSystem
Create a relay integrator with hysteresis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
float
|
Magnitude of the integrator rate. |
1.0
|
lower
|
float
|
Lower switching threshold. |
-1.0
|
upper
|
float
|
Upper switching threshold. |
1.0
|
initial_state
|
ndarray | None
|
Optional initial scalar state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured with relay dynamics. |
switched_linear
¶
switched_linear(a_on: ndarray | None = None, a_off: ndarray | None = None, threshold: float = 0.0, initial_state: ndarray | None = None) -> HybridSystem
Create a switched linear system benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a_on
|
ndarray | None
|
Dynamics matrix for the "on" location. |
None
|
a_off
|
ndarray | None
|
Dynamics matrix for the "off" location. |
None
|
threshold
|
float
|
Switching threshold on x[0]. |
0.0
|
initial_state
|
ndarray | None
|
Optional initial state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured with switching linear dynamics. |
tank_valves
¶
tank_valves(area_1: float = 1.0, area_2: float = 1.2, inflow: float = 0.02, *, outlet_area_1: float = 0.002, outlet_area_2: float = 0.012, valve_gain: float = 0.01, high_level: float = 1.2, low_level: float = 0.4, gravity: float = 9.81, initial_state: Sequence[float] | ndarray | None = None) -> HybridSystem
Create a gravity-drained two-tank benchmark.
Both tanks have atmospheric free surfaces and share a bottom datum. An always-on pump feeds tank 1. A check valve permits gravity-driven transfer from tank 1 to tank 2 only while open; overflow is not modeled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_1
|
float
|
Tank 1 cross-sectional area in square meters. |
1.0
|
area_2
|
float
|
Tank 2 cross-sectional area in square meters. |
1.2
|
inflow
|
float
|
Constant pump inflow to tank 1 in cubic meters per second. |
0.02
|
outlet_area_1
|
float
|
Tank 1 discharge coefficient times physical outlet area, in square meters. |
0.002
|
outlet_area_2
|
float
|
Tank 2 discharge coefficient times physical outlet area, in square meters. |
0.012
|
valve_gain
|
float
|
Effective open-valve transfer area in square meters. |
0.01
|
high_level
|
float
|
Tank 1 level in meters at which the valve opens. |
1.2
|
low_level
|
float
|
Tank 1 level in meters at which the valve closes. |
0.4
|
gravity
|
float
|
Gravitational acceleration in meters per second squared. |
9.81
|
initial_state
|
Sequence[float] | ndarray | None
|
Initial |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for valve-controlled gravity flow. |
References
The gravity-orifice basis follows the standard square-root head model described in https://www.mdpi.com/2076-3417/13/9/5414, Section 2.1.2.
thermostat
¶
thermostat(ambient: float = 20.0, heating_power: float = 5.0, cooling_rate: float = 0.3, hysteresis: float = 2.0, initial_state: ndarray | None = None) -> HybridSystem
Create a thermostat benchmark system.
Simulate with an explicit one-element finite input vector [target]
giving the target temperature at each requested time. The target sets
the high and low switching thresholds; flows depend only on state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ambient
|
float
|
Ambient temperature. |
20.0
|
heating_power
|
float
|
Heating input strength. |
5.0
|
cooling_rate
|
float
|
Cooling coefficient. |
0.3
|
hysteresis
|
float
|
Temperature hysteresis band width. |
2.0
|
initial_state
|
ndarray | None
|
Optional initial temperature. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem configured for thermostat switching. |
time_forced_switch
¶
time_forced_switch(period: float = 1.0, initial_state: ndarray | None = None) -> HybridSystem
Create a system that alternates between fast and slow decay.
The two physical state coordinates are unchanged at switches. Each location is active for half the period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
period
|
float
|
Full period of the two-location cycle. |
1.0
|
initial_state
|
ndarray | None
|
Optional initial [x1, x2] state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem with time-triggered switches. |
time_varying_event_surface
¶
time_varying_event_surface(*, hysteresis: float = 0.2, drift: float = 0.6, damping: float = 0.4, initial_state: ndarray | None = None) -> HybridSystem
Create a system with externally driven event-surface thresholds.
Simulate with an explicit finite one-element input vector [threshold].
Both switching surfaces use the supplied threshold at the queried time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hysteresis
|
float
|
Event-surface hysteresis width. |
0.2
|
drift
|
float
|
Drift magnitude per location. |
0.6
|
damping
|
float
|
Damping on the second state. |
0.4
|
initial_state
|
ndarray | None
|
Optional initial state. |
None
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem with time-dependent event surfaces. |
wind_turbine
¶
wind_turbine(*, initial_state: Sequence[float] | ndarray | None = None, pitch_kp: float = 0.01882681, pitch_ki: float = 0.008068634, speed_hysteresis: float = 0.2) -> HybridSystem
Model an already-running 5 MW-class wind turbine.
Wind turns the rotor; the generator resists rotation to extract power. Wind thrust bends the tower, modeled as a damped mass-spring system. Turning the blades out of the wind (pitching) reduces their loading. Startup, shutdown, and emergency-braking control are not modeled.
The state vector is [omega, x, v, beta, beta_rate, integral]:
omega: rotor speed (rad/s).x: tower displacement from its undeflected position (m).v: tower velocity (m/s).beta: blade pitch (rad).beta_rate: pitch rate (rad/s).integral: pitch controller's integral contribution (rad).
By default, the rotor turns at 0.65 rad/s and all other states are zero. The five locations set generator torque according to generator speed:
no_generation: zero torque; the rotor spins without generating.gradual_generation: torque increases linearly with speed.below_rated_power: torque is proportional to speed squared.approaching_rated_power: a second linear torque ramp.rated_power: constant power, with torque equal to power/speed.
Rated power is about 5.30 MW at the generator shaft; electrical output is
not modeled. Locations form a two-way chain in the order above. Upward
and downward speed thresholds differ by 2*speed_hysteresis; between
them, the current mode is retained. Switching leaves physical states unchanged.
Pitch control runs in every mode, using speed error and its accumulated
value (PI control). Blade motion lags the command, limited to 0..18 degrees.
The controller corrects accumulated error at those limits; they are not
mechanical stops.
Hardware and controller constants are recorded in system.parameters.
Supply simulate(..., input_stream=...) with a finite one-element
vector [wind] containing a positive wind speed (m/s), such as
lambda t: np.array([11.0]). Relative wind is
u = wind - v; it and rotor speed must stay positive. With radius R=63 m,
the tip-speed ratio omega*R/u must stay in 2.5..14.5 and pitch in
-2..20 degrees. Leaving the polynomial fit's domain raises an error.
The fit gives torque coefficient Cq and thrust coefficient Ct. Rotor
torque is rho*pi*R**3*u**2*Cq/2 and thrust rho*pi*R**2*u**2*Ct/2,
where rho is air density. Negative torque brakes the rotor. The rigid,
lossless gearbox makes generator speed 97*omega. Generator torque and
inertia are referred to the rotor by factors of 97 and 97 squared.
Generator torque has no delay or rate limit. Aerodynamics assume head-on
wind and an instantaneous response
to current conditions (a quasi-steady approximation); blades are rigid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initial_state
|
Sequence[float] | ndarray | None
|
Optional six-element running state (copied). |
None
|
pitch_kp
|
float
|
Positive finite pitch proportional gain (rad per rad/s). |
0.01882681
|
pitch_ki
|
float
|
Positive finite integral gain (rad per rad/s per second). |
0.008068634
|
speed_hysteresis
|
float
|
Positive generator-speed threshold halfwidth (rad/s). |
0.2
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
HybridSystem with five torque-control locations. |
valve_closure
¶
valve_closure(delay: float = 1.0) -> HybridSystem
Fill a tank until its inlet valve closes after threshold detection.
Water height rises at a constant rate until it reaches the closing threshold, which schedules valve closure. Filling continues during the delay; after closure the height stays constant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
delay
|
float
|
Finite, nonnegative closing delay in model time units. Use zero for immediate closure at the threshold. |
1.0
|
Returns:
| Type | Description |
|---|---|
HybridSystem
|
A two-location system with water height as its single state coordinate. |
HyDRA identification
¶
Curated HyDRA hybrid-system identification API.
Classes¶
HyDRACallback
¶
Observe discovery geometry and scalar fits; override any desired hooks.
Hooks run synchronously, and their exceptions propagate to the caller.
Methods:¶
start
¶
Begin discovery over trace_count independent traces.
threshold is the strict upper bound on absolute prediction error.
start_width and step_width are the initial candidate window
size and its growth step, measured in consecutive observations.
pending_segment_found
¶
pending_segment_found(segment: TraceSegment) -> None
Report the unassigned segment where candidate fitting will begin.
candidate_window_evaluated
¶
candidate_window_evaluated(*, segment: TraceSegment, fit: float) -> None
Report candidate accuracy on the observations in segment.
fit is the maximum absolute prediction error over that window.
candidate_selected
¶
candidate_selected(*, segment: TraceSegment, fit: float) -> None
Report the last evaluated window whose candidate met the threshold.
fit is its maximum absolute prediction error. The selected
candidate will next be used to group matching unassigned observations.
grouping_evaluated
¶
grouping_evaluated(*, flow_id: int, accepted_segments: tuple[TraceSegment, ...], considered_count: int) -> None
Report candidate acceptance across all traces, before final refitting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flow_id
|
int
|
ID reserved for this candidate if its final fit succeeds. |
required |
accepted_segments
|
tuple[TraceSegment, ...]
|
Previously unassigned observations that match the candidate within the error threshold. |
required |
considered_count
|
int
|
Total number of unassigned observations across all traces before grouping, including both accepted and rejected observations. Already assigned observations are excluded. |
required |
flow_finalized
¶
flow_finalized(*, flow_id: int, flow: LearnedFlow) -> None
Report a flow successfully refitted on its accepted observations.
flow_id is its position in result.flows. flow contains the
refitted model and the segments accepted before that refit.
finish
¶
finish(result: LearnedFlows) -> None
Receive the successful result assigning every supplied observation.
result is the same object returned by learn. This hook is not
called if identification, a backend, or an earlier callback fails.
HyDRAIdentificationError
¶
HyDRAIdentificationError(segment: TraceSegment, message: str)
Bases: RuntimeError
Discovery could not assign the supplied observations in a segment.
Attributes:
| Name | Type | Description |
|---|---|---|
segment |
TraceSegment
|
The failing trace segment, with half-open row bounds. |
Attributes¶
HyDRALearner
¶
HyDRALearner(regressor_factory: Callable[[], SupervisedLearner], threshold: float, start_width: int = 10, step_width: int = 5, callback: HyDRACallback | None = None)
Discover shared flow models from ordered traces with one scalar target.
HyDRA fits candidates on growing windows of consecutive observations, groups matching observations across traces, and refits each discovered flow on its accepted group. Supply derivatives as targets to learn rates of change, or another regression target to learn its shared behaviors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
regressor_factory
|
Callable[[], SupervisedLearner]
|
Creates a fresh batch |
required |
threshold
|
float
|
Finite, non-negative maximum absolute prediction error. Candidate acceptance uses the strict comparison error < threshold. |
required |
start_width
|
int
|
Positive initial window size in consecutive observations. |
10
|
step_width
|
int
|
Positive number of observations added at each window step. |
5
|
callback
|
HyDRACallback | None
|
Observer for discovery progress; defaults to a no-op |
None
|
Attributes¶
regressor_factory
instance-attribute
¶
regressor_factory: Callable[[], SupervisedLearner] = regressor_factory
callback
instance-attribute
¶
callback: HyDRACallback = callback if callback is not None else HyDRACallback()
Methods:¶
learn
¶
learn(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_features: Sequence[str]) -> LearnedFlows
Discover flows and return their models and observation membership.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Nonempty collection of nonempty Polars frames in observation order. Each frame is an independent trace. Additional columns can be retained when projecting the result onto these frames. |
required |
input_features
|
Sequence[str]
|
Nonempty, unique input column names. |
required |
output_features
|
Sequence[str]
|
Exactly one target column, distinct from inputs. |
required |
Selected columns must contain finite numeric values with no nulls.
flow_id is reserved for assignments and must be absent from frames
and feature lists. Selected observations and backend predictions use
Polars Float64 as their computational representation for fitting and
residuals, not as a precision improvement. Decimal values and large
integers can collapse to the same Float64 value. Original frames are
unchanged.
Returns:
| Type | Description |
|---|---|
LearnedFlows
|
Models and segments assigning every supplied observation to a flow. |
Raises:
| Type | Description |
|---|---|
ValueError
|
Data or feature roles violate the stated constraints. |
TypeError
|
A supplied trace is not a Polars DataFrame. |
HyDRAIdentificationError
|
A pending segment cannot be identified.
Its |
Backend and callback exceptions propagate to the caller.
LearnedFlow
dataclass
¶
LearnedFlow(model: Model, segments: tuple[TraceSegment, ...])
A fitted model and the observation segments accepted by its candidate.
Segments use half-open row bounds and are ordered by trace, then row. Membership is recorded before the final model refit.
Attributes¶
LearnedFlows
dataclass
¶
LearnedFlows(flows: tuple[LearnedFlow, ...], trace_lengths: tuple[int, ...], input_features: tuple[str, ...], output_features: tuple[str, ...])
Discovered flows and their membership in the supplied observations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flows
|
tuple[LearnedFlow, ...]
|
Fitted models with accepted segments. A flow's position is its shared flow ID across all traces. |
required |
trace_lengths
|
tuple[int, ...]
|
Row counts in the original trace order. |
required |
input_features
|
tuple[str, ...]
|
Ordered input column names used for fitting. |
required |
output_features
|
tuple[str, ...]
|
Output column names used for fitting. |
required |
Accepted segments cover every supplied observation exactly once.
Use to_labeled_frames(original_traces) to train a selector, or
to_flow_ids() for row-aligned NumPy assignments.
Attributes¶
Methods:¶
to_flow_ids
¶
Return fresh, fully populated int64 arrays of row assignments.
to_labeled_frames
¶
Add an Int64 flow_id column to each supplied frame.
Frames must match the original trace count, row counts, and positional order. All existing columns are preserved; every row has a flow ID.
TraceSegment
dataclass
¶
Consecutive observations within a trace: start inclusive, stop exclusive.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trace_index
|
int
|
Index in the supplied trace collection. |
required |
start
|
int
|
Index of the first included observation. |
required |
stop
|
int
|
Index just past the last observation. |
required |
HyDRAModel
¶
HyDRAModel(flow_models: list[Model], *, input_features: Sequence[str], output_features: Sequence[str], selector: HybridDecisionTreeModel | None = None, trace_schema: HyDRATraceSchema | None = None)
Bases: Model
Model composed of learned flow models and an optional selector.
A single-flow model predicts directly with that flow model. A multi-flow model needs a selector for batch prediction. Supply a trace schema for derivative rollout. Persistence uses pickle: load models only from trusted sources.
Attributes¶
Methods:¶
predict_with_diagnostics
¶
Route observations to flow models and retain row alignment details.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_features
|
DataFrame | LazyFrame
|
Frame containing all flow inputs and selector features. Flow inputs are converted to Float64 for prediction. |
required |
Returns:
| Type | Description |
|---|---|
HyDRABatchPrediction
|
Predicted outputs, original row indices, and selector decisions. |
HyDRABatchPrediction
|
History-based selectors begin after their required warmup rows. |
HyDRABatchPrediction
|
Selectors using previous-flow features require the stateful |
HyDRABatchPrediction
|
selector runtime. |
simulate
¶
simulate(t_span: tuple[float, float], x0: Iterable[float], *, input_stream: InputStream | None = None, include_inputs: bool = False, sample_times: Iterable[float] | None = None, sample_dt: float | None = None, rtol: float = 1e-07, atol: float = 1e-09, max_step: float | None = None) -> DataFrame
Integrate a learned derivative model on the requested time grid.
Requires a trace schema with matching state and derivative widths, and an input stream for any external inputs. Multi-flow models require a selector using current time, state, and external input features.
Select a flow at each grid point, including the final endpoint, and integrate it until the next point. The chosen grid therefore determines when flow changes can occur.
Returns:
| Type | Description |
|---|---|
DataFrame
|
Sampled frame with |
DataFrame
|
|
DataFrame
|
since it became active. With |
DataFrame
|
external input columns |
predict_next_state
¶
predict_next_state(state: Iterable[float], *, t: float, dt: float, input_stream: InputStream | None = None, rtol: float = 1e-07, atol: float = 1e-09, max_step: float | None = None) -> ndarray
PlotCallback
¶
PlotCallback(frame: DataFrame, *, columns: Sequence[str], time_column: str | None = None, trace_index: int = 0, ax: Axes | None = None, show: bool = True, pause: float = 0.001)
Bases: HyDRACallback
Plot one selected trace and its discovery segments.
Finalized flows use plain bands; provisional grouping uses hatching and the active window uses a dotted outline. Singleton rows remain visible as markers. Flow colors belong to this identification result, not to native locations or another fit. Only overlays owned by this callback are removed.
show=True enables live display and GUI event processing, with pause
controlling the update interval. show=False only updates artists:
no redraw, show, pause, or GUI event-loop calls are made. The caller owns
drawing or saving the resulting canvas and closing the figure.
Attributes¶
Methods:¶
candidate_window_evaluated
¶
candidate_window_evaluated(*, segment: TraceSegment, fit: float) -> None
grouping_evaluated
¶
grouping_evaluated(*, flow_id: int, accepted_segments: tuple[TraceSegment, ...], considered_count: int) -> None
HyDRATraceSchema
dataclass
¶
HyDRATraceSchema(time: str, state: tuple[str, ...], derivative: tuple[str, ...], inputs: tuple[str, ...] = ())
Column roles for a learned derivative model and its rollout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time
|
str
|
Time column name. |
required |
state
|
tuple[str, ...]
|
State column names used as model inputs. |
required |
derivative
|
tuple[str, ...]
|
Derivative column names used as model outputs, in the same order as their corresponding state columns. |
required |
inputs
|
tuple[str, ...]
|
Optional external input column names. |
()
|
All column names must be distinct. input_features collects the time,
state, and external input columns. Rollout requires equal numbers of
state and derivative columns.
FlowPredictionResult
dataclass
¶
FlowPredictionResult(ready: bool, flow_id: int | None, probabilities: dict[int, float] = dict(), leaf_id: int | None = None, flow_model: Model | None = None)
HybridDecisionTreeLearner
¶
HybridDecisionTreeLearner(feature_config: SelectorFeatureConfig, **tree_kwargs: Any)
Bases: SupervisedLearner
Attributes¶
classifier
instance-attribute
¶
Methods:¶
learn
¶
learn(inputs: DataFrame | LazyFrame, outputs: DataFrame | LazyFrame) -> HybridDecisionTreeModel
learn_from_traces
¶
learn_from_traces(traces: list[DataFrame], flow_models_by_id: dict[int, Model] | None = None) -> HybridDecisionTreeModel
HybridDecisionTreeModel
¶
HybridDecisionTreeModel(classifier: DecisionTreeClassifier, feature_columns: tuple[str, ...], feature_config: SelectorFeatureConfig, flow_models_by_id: dict[int, Model] | None = None)
Bases: Model
Attributes¶
Methods:¶
predict_details
¶
predict_details(input_features: DataFrame | LazyFrame) -> list[FlowPredictionResult]
SelectorEvaluationReport
dataclass
¶
SelectorFeatureConfig
dataclass
¶
SelectorFeatureConfig(state_features: tuple[str, ...] = (), input_features: tuple[str, ...] = (), derivative_features: tuple[str, ...] = (), state_history: int = 0, input_history: int = 0, derivative_history: int = 0, flow_history: int = 0)
Configuration for selector feature construction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_features
|
tuple[str, ...]
|
State columns available to the selector. |
()
|
input_features
|
tuple[str, ...]
|
Raw input columns available to the selector. |
()
|
derivative_features
|
tuple[str, ...]
|
Derivative columns available to the selector. |
()
|
state_history
|
int
|
Number of previous state rows used for selector features. |
0
|
input_history
|
int
|
Number of previous input rows used for selector features. |
0
|
derivative_history
|
int
|
Number of previous derivative rows used for selector features. |
0
|
flow_history
|
int
|
Number of previous flow IDs used for selector features. |
0
|
SelectorFlowInspection
dataclass
¶
SelectorInspection
dataclass
¶
SelectorInspection(feature_columns: tuple[str, ...], classes: tuple[int, ...], max_depth: int, n_leaves: int, nodes: tuple[SelectorNodeInspection, ...], leaves: tuple[SelectorLeafInspection, ...], flows: tuple[SelectorFlowInspection, ...])
Attributes¶
SelectorLeafInspection
dataclass
¶
SelectorNodeInspection
dataclass
¶
SelectorNodeInspection(node_id: int, sample_count: int, impurity: float, is_leaf: bool, predicted_flow_id: int, weighted_class_support: dict[int, float], feature_index: int | None = None, feature_name: str | None = None, threshold: float | None = None, left_child_id: int | None = None, right_child_id: int | None = None)
Attributes¶
StatefulHybridDecisionTreeSelector
¶
StatefulHybridDecisionTreeSelector(model: HybridDecisionTreeModel, seed_flows: Sequence[int] = ())
Functions:¶
evaluate_selector_autoregressive
¶
evaluate_selector_autoregressive(model: HybridDecisionTreeModel, traces: list[DataFrame], seed_flows: tuple[int, ...] | list[int] = ()) -> SelectorEvaluationReport
evaluate_selector_oracle
¶
evaluate_selector_oracle(model: HybridDecisionTreeModel, traces: list[DataFrame]) -> SelectorEvaluationReport
Dainarx identification
¶
Noise-free sampled NARX hybrid-system identification.
Type Aliases¶
NonlinearTerm
¶
A nonlinear regression feature called as term(history, current_input).
history has shape (order, n_outputs), oldest observation first;
current_input has shape (n_inputs,) and is aligned with the output
being predicted. Here n_outputs and n_inputs count the selected
output and input features. Columns follow their selected feature order.
Return a (n_outputs,) vector in output-feature order, with one
feature value for each output's regression.
Classes¶
NarxModel
dataclass
¶
NarxModel(order: int, input_features: tuple[str, ...], output_features: tuple[str, ...], time_column: str, dt: float, nonlinear_terms: tuple[NonlinearTerm, ...], coefficients: FloatArray, rmse: float)
A nonlinear autoregressive model with exogenous inputs (NARX).
Predict the next output from previous outputs and the input at the row
being predicted. Let n_outputs and n_inputs count the selected
output and input features; n_coefficients = len(nonlinear_terms) +
order * n_outputs + n_inputs + 1.
Attributes:
| Name | Type | Description |
|---|---|---|
order |
int
|
Number of previous output observations used for prediction. |
input_features |
tuple[str, ...]
|
Selected input names, in input-vector order. |
output_features |
tuple[str, ...]
|
Selected output names, in output-vector order. |
time_column |
str
|
Timestamp column name. |
dt |
float
|
Sampling period in timestamp units. |
nonlinear_terms |
tuple[NonlinearTerm, ...]
|
Ordered nonlinear regression features; see NonlinearTerm. |
coefficients |
FloatArray
|
Array of shape |
rmse |
float
|
Largest root-mean-square error (RMSE) among the outputs, measured on training targets in output units. This is not a guarantee of future prediction error. |
Attributes¶
Methods:¶
predict
¶
Predict the next output from output history and its aligned input.
n_outputs and n_inputs count the selected output and input
features, respectively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
history
|
ArrayLike
|
Output history of shape |
required |
current_input
|
ArrayLike
|
A |
required |
Returns:
| Type | Description |
|---|---|
FloatArray
|
A |
DainarxDynamics
dataclass
¶
DainarxDynamics(locations: tuple[NarxModel, ...], segments: tuple[DainarxSegment, ...], trace_lengths: tuple[int, ...], eta: float)
Complete dynamics discovery and original-observation membership.
This is not an executable hybrid model: guards and post-switch predictors
still require transition fitting, which can fail independently. Membership
is discovered, not ground truth or a per-row RMSE certificate. The first
order rows of each segment are labeled even though ordinary location
fitting excludes them. Adjacent segments may share a location ID; retain
their detected boundaries even when projected labels do not change.
Attributes:
| Name | Type | Description |
|---|---|---|
locations |
tuple[NarxModel, ...]
|
Ordinary NARX models indexed by contiguous location IDs. All share history order, selected feature order, time column, sampling period, and ordered nonlinear feature functions. |
segments |
tuple[DainarxSegment, ...]
|
Half-open original-frame intervals |
trace_lengths |
tuple[int, ...]
|
Original observation counts in input trace order. |
eta |
float
|
Nonnegative maximum per-output training RMSE threshold, in output units, not a rollout error bound. |
Attributes¶
Methods:¶
to_location_ids
¶
Project discovered membership onto all original observation rows.
Returns:
| Type | Description |
|---|---|
list[ndarray]
|
Fresh, fully populated int64 arrays of shape |
to_labeled_frames
¶
Add discovered location_id membership to original observations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Original Polars frames in discovery order. Keep the original row correspondence; observation identity is not verified by the trace-length checks. |
required |
Returns:
| Type | Description |
|---|---|
list[DataFrame]
|
Independent frames preserving every original column, column order,
and dtype, followed by a non-null Int64 |
DainarxFit
dataclass
¶
DainarxFit(model: DainarxModel, segments: tuple[DainarxSegment, ...], eta: float)
The executable model, complete segmentation, and acceptance threshold.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
DainarxModel
|
Learned locations, guards, and post-switch predictors. |
segments |
tuple[DainarxSegment, ...]
|
Half-open original-frame intervals |
eta |
float
|
Fitting acceptance threshold in output units, not a rollout error bound. |
Attributes¶
DainarxSegment
dataclass
¶
A half-open sampled-trace interval and its learned location ID.
Attributes:
| Name | Type | Description |
|---|---|---|
trace_index |
int
|
Index into the input trace collection. |
start |
int
|
Inclusive row index in the original trace frame. |
stop |
int
|
Exclusive row index in the original trace frame. |
location_id |
int
|
Learned location ID shared across traces, indexing
|
DainarxModel
dataclass
¶
DainarxModel(locations: tuple[NarxModel, ...], transitions: tuple[DainarxTransition, ...])
Learned sampled dynamics and transitions between location IDs.
Attributes:
| Name | Type | Description |
|---|---|---|
locations |
tuple[NarxModel, ...]
|
NARX models indexed by contiguous location IDs
|
transitions |
tuple[DainarxTransition, ...]
|
Guarded edges whose source and destination are IDs
indexing |
Attributes¶
Methods:¶
rollout
¶
Recursively predict outputs from exactly order seed rows.
At row t, guards inspect output[t] and input[t]; a switch
affects row t + 1. Its predictor uses the preceding output history
and input[t + 1]. Post-switch predictors run in tuple order before
destination dynamics resume. Multiple enabled guards or an interrupted
reset sequence raise ValueError; no priority is invented.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
DataFrame
|
Polars frame with known timestamps and selected inputs
for all rows, including seeds, at the models' shared sampling
period. Unrelated columns are ignored. Exactly |
required |
output_history
|
ArrayLike
|
Seed array of shape |
required |
initial_location
|
int
|
Learned location ID at the last seed row, which must not be inside an unfinished reset sequence. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
A Polars frame with the timestamp column, selected output columns,
and |
DainarxTransition
dataclass
¶
DainarxTransition(source: int, destination: int, guard: SVC, reset_predictors: tuple[NarxModel, ...])
An output-and-input SVM guard and ordered post-switch predictors.
Attributes:
| Name | Type | Description |
|---|---|---|
source |
int
|
Source location ID, indexing |
destination |
int
|
Destination location ID, indexing |
guard |
SVC
|
Fitted SVM whose feature vector is selected outputs followed
by selected inputs, in their feature order. |
reset_predictors |
tuple[NarxModel, ...]
|
Predictors for the first |
Functions:¶
fit_narx
¶
fit_narx(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_features: Sequence[str], time_column: str, order: int, nonlinear_terms: Sequence[NonlinearTerm] = ()) -> NarxModel
Fit one NARX model to predict the next output from history and inputs.
Use ordinary least squares without crossing trace histories. Each trace
needs more than order observations; pooled targets must number at
least n_coefficients = len(nonlinear_terms) + order * n_outputs +
n_inputs + 1, where n_outputs and n_inputs count selected
features. Rank-deficient fits are allowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent Polars frames sampled at the same interval. Start times and lengths may differ; only selected columns are used. |
required |
input_features
|
Sequence[str]
|
Input column names, in input-vector order. |
required |
output_features
|
Sequence[str]
|
Output column names, in output-vector order. |
required |
time_column
|
str
|
Timestamp column, increasing within each trace. |
required |
order
|
int
|
Number of previous output observations used for prediction. |
required |
nonlinear_terms
|
Sequence[NonlinearTerm]
|
Ordered nonlinear regression features following the NonlinearTerm contract. |
()
|
Returns:
| Type | Description |
|---|---|
NarxModel
|
A NarxModel with fitted coefficients and maximum per-output training RMSE. |
fit_dainarx
¶
fit_dainarx(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_features: Sequence[str], time_column: str, order: int, window: int, guard_kernel: Literal['linear', 'poly', 'rbf', 'sigmoid'], guard_c: float, guard_gamma: Literal['scale', 'auto'] | float, nonlinear_terms: Sequence[NonlinearTerm] = (), eta: float | None = None, guard_degree: int = 3, guard_coef0: float = 0.0) -> DainarxFit
Infer sampled NARX locations, SVM guards, and post-switch predictors.
Compose fit_dainarx_dynamics and fit_dainarx_transitions into a
complete executable model. Use those stages separately to inspect dynamics
and annotate observations even when transition fitting later fails.
Fitting acceptance uses the largest per-output root-mean-square error (RMSE), rather than the paper's unnormalized L2 residual. By default, the threshold is scaled from the shared sampling period and the largest absolute selected training output across all traces. See the Dainarx guide for the noise-free method and usage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent Polars frames sampled at the same interval. Start times and lengths may differ; only selected columns are used. |
required |
input_features
|
Sequence[str]
|
Input column names, in input-vector order. |
required |
output_features
|
Sequence[str]
|
Output column names, in output-vector order. |
required |
time_column
|
str
|
Timestamp column, increasing within each trace. |
required |
order
|
int
|
Number of previous output observations used for prediction. |
required |
window
|
int
|
Number of observations per screening window, not regression
targets. Each trace needs at least this many observations, and
|
required |
guard_kernel
|
Literal['linear', 'poly', 'rbf', 'sigmoid']
|
SVM kernel: |
required |
guard_c
|
float
|
Positive inverse regularization strength; larger values penalize training misclassification more. |
required |
guard_gamma
|
Literal['scale', 'auto'] | float
|
Nonnegative scale coefficient for |
required |
nonlinear_terms
|
Sequence[NonlinearTerm]
|
Ordered nonlinear regression features following the NonlinearTerm contract. Linear terms already include all outputs at every lag, current inputs, and bias. |
()
|
eta
|
float | None
|
Nonnegative acceptance threshold in output units for the
maximum per-output training RMSE. Applies to screening, clustering,
location fits, and reset fits; it is not a rollout error guarantee.
|
None
|
guard_degree
|
int
|
Nonnegative polynomial degree, used only for |
3
|
guard_coef0
|
float
|
Additive constant in the |
0.0
|
Returns:
| Type | Description |
|---|---|
DainarxFit
|
The learned model, complete segmentation, and fitting threshold in a DainarxFit. |
Whole segments need more targets than the per-output coefficient count,
len(nonlinear_terms) + order * len(output_features) +
len(input_features) + 1. Each reset phase needs at least that many
transition occurrences. Insufficient or rejected segment/reset/guard
evidence, or a detected same-location boundary within a trace, raises
ValueError rather than returning a partial model. Rank deficiency
alone is allowed.
fit_dainarx_dynamics
¶
fit_dainarx_dynamics(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_features: Sequence[str], time_column: str, order: int, window: int, nonlinear_terms: Sequence[NonlinearTerm] = (), eta: float | None = None) -> DainarxDynamics
Discover ordinary NARX locations and complete observation membership.
Screen segments, cluster pooled regression data, and fit ordinary location
models using maximum per-output training RMSE acceptance. Each segment's
first order rows are excluded from ordinary fitting, but included in
annotations. Same-location boundaries are retained, not coalesced or
rejected here: transition fitting determines whether resets are supported.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent Polars frames sampled at the same interval. Start times and lengths may differ; only selected columns are used. |
required |
input_features
|
Sequence[str]
|
Input column names, in input-vector order. |
required |
output_features
|
Sequence[str]
|
Output column names, in output-vector order. |
required |
time_column
|
str
|
Timestamp column, increasing within each trace. |
required |
order
|
int
|
Number of preceding output observations used for prediction. |
required |
window
|
int
|
Screening window observation count. Each trace needs at least
this many rows; |
required |
nonlinear_terms
|
Sequence[NonlinearTerm]
|
Ordered regression feature functions following the NonlinearTerm contract. |
()
|
eta
|
float | None
|
Nonnegative maximum per-output training RMSE threshold in
output units. |
None
|
Returns:
| Type | Description |
|---|---|
DainarxDynamics
|
A DainarxDynamics result containing location predictors and original-trace assignments. |
fit_dainarx_transitions
¶
fit_dainarx_transitions(traces: Sequence[DataFrame], dynamics: DainarxDynamics, *, guard_kernel: Literal['linear', 'poly', 'rbf', 'sigmoid'], guard_c: float, guard_gamma: Literal['scale', 'auto'] | float, guard_degree: int = 3, guard_coef0: float = 0.0) -> tuple[DainarxTransition, ...]
Learn guards and post-switch predictors without repeating discovery.
Guards use current selected outputs followed by current selected inputs. Each source-conditioned binary target asks whether the next observation belongs to the edge's destination. Reset predictors use preceding output history and the input aligned with the target row, one per post-switch phase. They share ordinary fitting's coefficient-count and RMSE gates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Original observation frames, in the same trace and row order used for discovery. Matching lengths alone do not establish that the observations are unchanged. |
required |
dynamics
|
DainarxDynamics
|
Complete discovery result supplying locations, segment provenance, trace lengths, feature template, and acceptance eta. |
required |
guard_kernel
|
Literal['linear', 'poly', 'rbf', 'sigmoid']
|
SVM kernel: |
required |
guard_c
|
float
|
Positive inverse regularization strength. |
required |
guard_gamma
|
Literal['scale', 'auto'] | float
|
Nonnegative kernel scale coefficient or |
required |
guard_degree
|
int
|
Nonnegative polynomial degree, used only for |
3
|
guard_coef0
|
float
|
Additive constant for |
0.0
|
Returns:
| Type | Description |
|---|---|
tuple[DainarxTransition, ...]
|
Learned transitions, ready for |
Raises:
| Type | Description |
|---|---|
ValueError
|
Transition evidence is insufficient or rejected, or a detected boundary has the same location on both sides. |
FaMoS-DTW identification
¶
FaMoS-DTW discovery, affine sampled dynamics, and next-location trees.
Classes¶
FamosDiscovery
dataclass
¶
FamosDiscovery(segments: tuple[FamosSegment, ...], trace_lengths: tuple[int, ...], input_features: tuple[str, ...], output_features: tuple[str, ...], time_column: str, dt: float, input_scales: FloatArray, output_scales: FloatArray, location_keys: tuple[tuple[int, ...], ...])
Discovery independent of dynamics or transition fitting.
Attributes:
| Name | Type | Description |
|---|---|---|
segments |
tuple[FamosSegment, ...]
|
Intervals in trace/row order. Boundaries remain distinct even when adjacent intervals have the same location. |
trace_lengths |
tuple[int, ...]
|
Original training-frame lengths, including warmup. |
input_features |
tuple[str, ...]
|
Input column names in vector order. |
output_features |
tuple[str, ...]
|
Output column names in vector order. |
time_column |
str
|
Timestamp column name. |
dt |
float
|
Shared sampling period. |
input_scales |
FloatArray
|
Training maximum absolute inputs, with zero replaced by one. Prediction uses these scales without clipping. |
output_scales |
FloatArray
|
Corresponding training maximum absolute outputs. This follows the artifact's evaluation entry point; its trace entry point instead uses max-positive output scaling. |
location_keys |
tuple[tuple[int, ...], ...]
|
Global IDs index tuples of per-signal cluster IDs, ordered as outputs followed by inputs. |
Attributes¶
Methods:¶
to_labeled_frames
¶
Append nullable location_id to the original training frames.
Supply the same observations, in the same trace and row order used for discovery. This is a provenance projection, not a classifier for new traces. Frames are not resampled or truncated. An existing location_id column raises ValueError rather than being overwritten.
FamosSegment
dataclass
¶
One original-frame half-open interval, including unassigned evidence.
Attributes:
| Name | Type | Description |
|---|---|---|
trace_index |
int
|
Index in the independent training-trace sequence. |
start |
int
|
First original row, inclusive. |
stop |
int
|
Last original row, exclusive. |
location_id |
int | None
|
Global location ID, or None for derivative warmup or a segment too short for trimmed DTW comparison. |
FamosDynamics
dataclass
¶
FamosDynamics(discovery: FamosDiscovery, order: int, coefficients: tuple[FloatArray, ...], sample_counts: tuple[int, ...], ranks: tuple[int, ...], rmse: tuple[FloatArray, ...])
Output-only affine ARX models with exact output-history shifting.
This intentionally fits only the next output, not the artifact's freely fitted augmented lag-state equations. Derivatives are not targets.
Attributes:
| Name | Type | Description |
|---|---|---|
discovery |
FamosDiscovery
|
Independent discovery artifact providing feature order, sampling period, training scales, and location IDs. |
order |
int
|
Number of output-history rows, not derivative order. |
coefficients |
tuple[FloatArray, ...]
|
One array per location of shape
|
sample_counts |
tuple[int, ...]
|
Number of within-segment regression targets per location. |
ranks |
tuple[int, ...]
|
Least-squares design ranks. Rank deficiency is reported, not rejected, and does not establish parameter identifiability. |
rmse |
tuple[FloatArray, ...]
|
Per-location arrays of per-output training RMSE in physical units, not an accuracy acceptance criterion. |
Attributes¶
Methods:¶
predict
¶
Predict y[t+1] using q[t], history through t, and u[t].
Supply physical-unit output_history of shape (order, n_outputs),
oldest first, and inputs of shape (n_inputs,). Column order is
defined by discovery. Return a physical-unit (n_outputs,) vector.
Stored training scales are used without clipping.
FamosModel
dataclass
¶
FamosModel(dynamics: FamosDynamics, selector: FamosTransitionSelector)
Compose sampled dynamics and next-location selection.
Feature order and location count must match. The caller is responsible for matching location meanings and source-time training alignment; structural checks cannot certify provenance. Each artifact uses its own training scales and exchanges physical-unit values, so scales may differ.
Attributes:
| Name | Type | Description |
|---|---|---|
dynamics |
FamosDynamics
|
Affine output-history dynamics. |
selector |
FamosTransitionSelector
|
Source-conditioned next-location trees with matching output/input feature order and location count. |
Attributes¶
Methods:¶
rollout
¶
Predict recursively using old-location dynamics before advancing.
Compute BOTH y[t+1] and q[t+1] from q[t], y through t, and u[t]. A row's location governs the interval beginning at that row. This source-input convention intentionally corrects the artifact's use of target-time inputs during rollout despite source-time fitting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
DataFrame
|
N timestamp/input rows at discovery.dt; unrelated columns are ignored. Return N rows, including seeds. The final input is unused. N must be at least order; seed-only is supported. |
required |
output_history
|
ArrayLike
|
Exactly |
required |
initial_location
|
int
|
Learned ID at the LAST seed row. Earlier seed location IDs are null. Initialization is the caller's choice, not inferred by clustering a held-out trajectory. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Timestamp, output columns and nullable location_id. The last |
DataFrame
|
predicted location is included even without a following output. |
FamosTransitionSelector
dataclass
¶
FamosTransitionSelector(output_features: tuple[str, ...], input_features: tuple[str, ...], n_locations: int, output_scales: FloatArray, input_scales: FloatArray, trees: tuple[DecisionTreeClassifier, ...])
One inspectable next-location tree per categorical source location.
Explicit source dispatch differs from the artifact's single joint categorical tree; numeric location IDs are never ordered input features.
Attributes:
| Name | Type | Description |
|---|---|---|
output_features |
tuple[str, ...]
|
Current observed physical outputs in vector order. |
input_features |
tuple[str, ...]
|
Current exogenous controls in vector order. |
n_locations |
int
|
Declared number of categorical locations. |
output_scales |
FloatArray
|
Training maximum absolute outputs, zero replaced by one. |
input_scales |
FloatArray
|
Corresponding training maximum absolute inputs. |
trees |
tuple[DecisionTreeClassifier, ...]
|
Source-indexed fitted sklearn trees. Features are normalized current outputs followed by current inputs; targets are next-row location IDs, including self transitions. |
Functions:¶
discover_famos_locations
¶
discover_famos_locations(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_features: Sequence[str], window: int, trim: int, peak_height: float, rho_min: float, rho_max: float, time_column: str = 't', max_derivative: int = 1, clustering_derivative: int = 1, alpha: float = 2.0) -> FamosDiscovery
Discover sampled locations without fitting any dynamics.
Each signal is segmented geometrically, then clustered by exact DTW. Global locations are tuples of per-signal clusters, not connected components of a distance graph. Ordered asymmetric reassignment follows trace/row order. The DTW backend is dtaidistance's exact C implementation; MATLAB optimal-path tie equivalence is not claimed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent, uniformly sampled eager frames. Only selected columns are read; differences and histories restart per trace. |
required |
input_features
|
Sequence[str]
|
Inputs used raw for detection and clustering. |
required |
output_features
|
Sequence[str]
|
Outputs used raw and through max_derivative for recursive detection, and at clustering_derivative for DTW. |
required |
window
|
int
|
Rows on EACH side of a candidate, excluding its center. Each window is offset by its first value before L1 comparison. Recursion insets peak-adjacent ends by floor(window / 2), retaining the outer trace endpoints. |
required |
trim
|
int
|
Rows removed at each segment end for DTW only. Fewer than two remaining rows leaves the segment unassigned. |
required |
peak_height
|
float
|
Minimum geometric L1 peak height in scaled signals. |
required |
rho_min
|
float
|
Lower bound on a row's DTW acceptance threshold. |
required |
rho_max
|
float
|
Upper bound on that threshold. |
required |
time_column
|
str
|
Timestamp column, not a regression feature. |
't'
|
max_derivative
|
int
|
Highest repeated backward difference divided by dt. The first max_derivative rows remain unassigned, not padded. |
1
|
clustering_derivative
|
int
|
Output derivative used for DTW; zero removes offsets from start/end-matched windows, higher orders retain them. |
1
|
alpha
|
float
|
Threshold multiplier of each segment's nearest-other score, clipped to [rho_min, rho_max]. |
2.0
|
Returns:
| Type | Description |
|---|---|
FamosDiscovery
|
Partial location labels and training-only scales, retaining original |
FamosDiscovery
|
trace boundaries. No accuracy, rank, or full-coverage gate is applied. |
fit_famos_dynamics
¶
fit_famos_dynamics(traces: Sequence[DataFrame], discovery: FamosDiscovery, *, order: int = 1) -> FamosDynamics
Fit y[t+1] = f_qt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Original discovery frames in the same trace/row order. |
required |
discovery
|
FamosDiscovery
|
Partial location assignments; null rows are not fitted. |
required |
order
|
int
|
Number of output-history rows. History may cross location boundaries but never trace boundaries. Source and target must lie in the SAME discovered segment, even if adjacent segments share a label. |
1
|
Returns:
| Type | Description |
|---|---|
FamosDynamics
|
Least-squares dynamics and diagnostics without a rank or RMSE gate. |
FamosDynamics
|
A location with no usable regression targets raises ValueError; |
FamosDynamics
|
the independent discovery artifact remains usable. |
fit_famos_transitions
¶
fit_famos_transitions(traces: Sequence[DataFrame], *, output_features: Sequence[str], input_features: Sequence[str], n_locations: int, max_depth: int | None = None, min_samples_leaf: int = 1, random_state: int = 0) -> FamosTransitionSelector
Fit next-location trees from adjacent explicitly labeled training rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent eager frames in caller-supplied row order, with
nullable integer |
required |
output_features
|
Sequence[str]
|
Ordered current observed physical outputs, not next-row targets. At least one output is required. |
required |
input_features
|
Sequence[str]
|
Ordered current exogenous controls; may be empty.
Feature names must be unique and cannot include |
required |
n_locations
|
int
|
Positive number of declared source locations. The caller is responsible for location meanings and source-time alignment. |
required |
max_depth
|
int | None
|
Maximum depth of each source's sklearn decision tree. |
None
|
min_samples_leaf
|
int
|
Minimum training observations in each tree leaf. |
1
|
random_state
|
int
|
Seed controlling sklearn split tie breaking. |
0
|
Returns:
| Type | Description |
|---|---|
FamosTransitionSelector
|
Source-conditioned trees, including valid constant-target trees, |
FamosTransitionSelector
|
with training-only maximum-absolute feature scales (zero becomes one). |
FamosTransitionSelector
|
A source without any outgoing training pair raises ValueError |
FamosTransitionSelector
|
rather than acquiring an invented self loop. |
MMSR Identification
¶
Multi-modal symbolic regression for known-K scalar input-output mappings.
CSR jointly learns symbolic expressions, soft memberships, and variances.
Subsequent transition modeling learns source-conditioned symbolic guards.
Both stages return inspectable artifacts, not a universal hybrid runtime.
PySR/Julia are loaded only when fitting, via the pysr optional extra.
Classes¶
MMSRCSR
dataclass
¶
MMSRCSR(input_features: tuple[str, ...], output_feature: str, variances: _Array, memberships: tuple[_Array, ...], diagnostics: CSRDiagnostics, _functions: tuple[_Expression, ...])
Scalar symbolic mappings, residual variances, and soft memberships.
Locations are positional integers in expressions and variances.
memberships[i] has shape (trace_i.height, n_locations) in the
original row order. No observations are dropped. Variances are in squared
output units. Expressions alone do not classify locations: posterior
membership also requires the observed target.
Attributes¶
complexities
property
¶
Original search-tree node counts, before SymPy simplification.
Methods:¶
predict
¶
Evaluate every mapping; return shape (n_rows, n_locations).
Only input_features are read. Nonfinite expression outputs raise
ValueError, including rational singularities outside training.
membership
¶
Evaluate posterior memberships from regressors AND observed targets.
Returns shape (n_rows, n_locations). This is not an input-only
location classifier and does not perform temporal inference.
CSRDiagnostics
dataclass
¶
CSRDiagnostics(completed_sweeps: int, updates: int, stop_reason: Literal['max_sweeps', 'error_stable', 'no_usable_candidate'], error: float, aic: float, failed_location: int | None = None)
Stopping information, not a certificate of distinct location recovery.
updates counts committed sequential replacements, excluding
initialization. error is the final responsibility-weighted sum of
absolute residuals; aic uses the total original tree complexity.
failed_location is set only for an entirely unusable update frontier.
MMSRTransition
dataclass
¶
MMSRTransition(source_location: int, destination_location: int, loss: float, aic: float, _function: _Expression)
One ordered nonself edge with raw expression and zero-threshold guard.
loss is the source-balanced weighted mean squared error of the sigmoid
of expression against the soft successor destination membership.
complexity counts the original raw search tree, not the fixed sigmoid.
Attributes¶
expression
property
¶
Raw symbolic expression, without the training sigmoid wrapper.
Methods:¶
guard
¶
Evaluate expression >= 0 at source-time regressors.
Raises ValueError for nonfinite expression outputs. An active guard
is not a physical reset or an event-localized continuous-time switch.
MMSRTransitions
dataclass
¶
MMSRTransitions(input_features: tuple[str, ...], edges: tuple[MMSRTransition, ...])
All ordered nonself edges; no implicit self edges or execution policy.
Multiple outgoing guards may be active together. Arbitration and probability propagation are deliberately not defined by this artifact.
Attributes¶
Functions:¶
fit_mmsr_csr
¶
fit_mmsr_csr(traces: Sequence[DataFrame], *, input_features: Sequence[str], output_feature: str, n_locations: int, search_factory: _SearchFactory, max_sweeps: int = 10, random_state: int = 0, variance_floor: float = 1e-12) -> MMSRCSR
Jointly fit symbolic mappings, memberships, and residual variances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
Independent eager frames of aligned scalar regression rows. Inputs may be measured states. No time, derivative, lag, or sampling period is inferred. Ordering matters only for subsequent TM. |
required |
input_features
|
Sequence[str]
|
Ordered regressor columns. |
required |
output_feature
|
str
|
One observed scalar target column. |
required |
n_locations
|
int
|
Known positive number of locations. |
required |
search_factory
|
_SearchFactory
|
Zero-argument factory returning a fresh configured
|
required |
max_sweeps
|
int
|
Finite cap on complete sequential update sweeps. |
10
|
random_state
|
int
|
Seed for Dirichlet(1, ..., 1) initial memberships, separate from the factory's search seeds. |
0
|
variance_floor
|
float
|
Positive floor in squared output units; no rescaling. |
1e-12
|
Returns:
| Type | Description |
|---|---|
MMSRCSR
|
Independently usable CSR artifact. A wholly unusable proposal frontier |
MMSRCSR
|
preserves the last valid mixture and reports |
MMSRCSR
|
invalid initialization or committed mixtures raise |
Notes
Follows Ly and Lipson's MMSR (JMLR 2012), with PySR replacing Eureqa.
Local search minimizes weighted mean log1p(abs(residual)).
Initialization interprets the paper's unspecified local AIC as
2*c + N*log(local_mean_loss). Updates select from the fresh frontier
by complete-mixture AIC with total tree complexity and the sum of
responsibility-weighted absolute residuals, not by local loss.
Gaussian memberships have variance prefactors and no frequency priors;
variances use the paper's unbiased weighted estimator, not MLE.
Finite searches can worsen the incumbent: no incumbent injection,
monotonicity gate, restart, or best-ever return is used. Five complete
sweeps with relative error change below 2% stop as error_stable;
collapsed or inaccurate mappings can also be stable. Numerical support
guards are 1e-12 for mass and variance denominator, effective sample
size must exceed 1 + 1e-10, and logarithms floor error at 1e-300.
fit_mmsr_transitions
¶
fit_mmsr_transitions(traces: Sequence[DataFrame], csr: MMSRCSR, *, search_factory: _SearchFactory) -> MMSRTransitions
Learn every source-conditioned next-location guard after CSR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
traces
|
Sequence[DataFrame]
|
The original ordered CSR traces. Adjacent rows are paired only within each independent trace; no timestamp or sampling period is inferred. Lengths and columns are checked, not dataset identity. |
required |
csr
|
MMSRCSR
|
Independently fitted CSR artifact with soft row memberships. |
required |
search_factory
|
_SearchFactory
|
Fresh configured |
required |
Returns:
| Type | Description |
|---|---|
MMSRTransitions
|
All K(K-1) directed nonself guards. For each source k and destination j, |
MMSRTransitions
|
the sigmoid of the raw expression at u_n targets gamma[j, n+1]. |
MMSRTransitions
|
Weights balance source departure against source stay, not destination |
MMSRTransitions
|
classes; all outgoing edges of one source share the same weights. |
MMSRTransitions
|
Selection uses |
Raises:
| Type | Description |
|---|---|
ValueError
|
Missing departure/stay evidence or an entirely unusable edge frontier, with the source or edge identified. No partial edge collection is returned; the separate CSR artifact remains usable. |
Notes
This explicitly chooses the paper's source-time TM alignment: u_n and location n predict location n+1. The paper's formal mapping convention elsewhere differs. No reset map, continuous-time model conversion, initialization rule, or general multi-edge rollout is inferred.