Skip to content
Oakfield Operator Calculus Function Reference Site

Nonlinear Operators

add_elementwise_math_operator(ctx, lhs, rhs, out, opts)

Perform elementwise discrete math operations including floor, fractional part, modulo, step functions, comparisons, and conditional selection. Essential for creating masks, quantization, and control flow in field processing.

add_elementwise_math_operator(ctx, lhs, [rhs], output, [options]) -> operator

Returns: Operator handle (userdata)

Note: The rhs field is optional and can be nil when using rhs_source = "constant".

Rounding Operations:

  • floor: out=x\text{out} = \lfloor x \rfloor
  • fract: out=xx[0,1)\text{out} = x - \lfloor x \rfloor \in [0, 1)

Modulo:

out=xyx/y\text{out} = x - y \cdot \lfloor x / y \rfloor

with guard against division by values smaller than epsilon.

Step Function:

out={true_valueif xthresholdfalse_valueotherwise\text{out} = \begin{cases} \text{true\_value} & \text{if } x \geq \text{threshold} \\ \text{false\_value} & \text{otherwise} \end{cases}

Comparisons:

  • eq: out=(xyϵ)  ?  true_value:false_value\text{out} = (|x - y| \leq \epsilon) \;?\; \text{true\_value} : \text{false\_value}
  • lt: out=(x<y)  ?  true_value:false_value\text{out} = (x < y) \;?\; \text{true\_value} : \text{false\_value}
  • gt: out=(x>y)  ?  true_value:false_value\text{out} = (x > y) \;?\; \text{true\_value} : \text{false\_value}

Conditional Select:

out={yif xthresholdfalse_valueotherwise\text{out} = \begin{cases} y & \text{if } x \geq \text{threshold} \\ \text{false\_value} & \text{otherwise} \end{cases}

Core Parameters:

ParameterTypeDefaultDescription
modeenum "floor"Operation to perform (see below)
rhs_sourceenum "constant"Source for rhs: field or constant
rhs_constantdouble 1.0Constant rhs value when rhs_source = "constant"
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Mode options: floor, fract, mod, step, eq, lt, gt, select

Comparison & Selection Parameters:

ParameterTypeDefaultRangeDescription
thresholddouble 0.0unboundedThreshold for step/select comparisons
epsilondouble 1e-6≥0Tolerance for eq comparisons and mod guards
true_valuedouble 1.0unboundedValue emitted when condition is true
false_valuedouble 0.0unboundedValue emitted when condition is false
  • Operates elementwise; no boundary handling required
  • For binary operations with rhs_source = "field", both fields must have matching dimensions
  • Complex fields: operations apply to real part only (use Complex Math for full complex operations)
  • All operations are unconditionally stable
  • floor/fract: Discontinuous at integers; may cause aliasing in smooth fields
  • mod: Protected against division by zero via epsilon guard
  • step: Discontinuous at threshold; consider smoothstep for continuous alternatives
  • Comparisons produce discrete 0/1-like outputs; consider smoothing for gradual transitions
  • Lightweight pointwise operations; O(N) complexity
  • Floor/fract use fast hardware intrinsics when available
  • Comparisons with rhs_source = "constant" avoid second field read
  • Select mode is branch-free on most architectures
-- Floor quantization
ooc.add_elementwise_math_operator(ctx, continuous, nil, quantized, {
mode = "floor"
})
-- Extract fractional part (sawtooth from ramp)
ooc.add_elementwise_math_operator(ctx, phase, nil, sawtooth, {
mode = "fract"
})
-- Modulo 3 wrapping
ooc.add_elementwise_math_operator(ctx, lhs, nil, out, {
mode = "mod",
rhs_constant = 3.0,
rhs_source = "constant"
})
-- Modulo with another field
ooc.add_elementwise_math_operator(ctx, lhs, divisor, out, {
mode = "mod",
rhs_source = "field"
})
-- Step function (threshold at 0.5)
ooc.add_elementwise_math_operator(ctx, signal, nil, binary, {
mode = "step",
threshold = 0.5,
true_value = 1.0,
false_value = 0.0
})
-- Field-vs-field comparison (greater than)
ooc.add_elementwise_math_operator(ctx, a, b, mask, {
mode = "gt",
rhs_source = "field"
})
-- Equality test with tolerance
ooc.add_elementwise_math_operator(ctx, measured, expected, match, {
mode = "eq",
rhs_source = "field",
epsilon = 0.01
})
-- Conditional select (choose values where mask >= 0.5)
ooc.add_elementwise_math_operator(ctx, mask, values, out, {
mode = "select",
threshold = 0.5,
rhs_source = "field",
false_value = 0.0
})
-- Create binary mask from continuous field
ooc.add_elementwise_math_operator(ctx, density, nil, mask, {
mode = "step",
threshold = 0.1,
true_value = 1.0,
false_value = 0.0
})

add_complex_math_operator(ctx, lhs, rhs, out, opts)

Perform elementwise complex arithmetic, transcendental functions, and component extraction. Supports full complex number operations with configurable scaling, bias, and phase wrapping.

add_complex_math_operator(ctx, lhs, [rhs], output, [options]) -> operator

Returns: Operator handle (userdata)

Note: The rhs field is optional for unary operations or when using rhs_source = "constant".

Binary Arithmetic:

For complex numbers z1=a+biz_1 = a + bi and z2=c+diz_2 = c + di:

  • add: z1+z2=(a+c)+(b+d)iz_1 + z_2 = (a+c) + (b+d)i
  • sub: z1z2=(ac)+(bd)iz_1 - z_2 = (a-c) + (b-d)i
  • mul: z1z2=(acbd)+(ad+bc)iz_1 \cdot z_2 = (ac - bd) + (ad + bc)i
  • div: z1/z2=ac+bdc2+d2+bcadc2+d2iz_1 / z_2 = \frac{ac + bd}{c^2 + d^2} + \frac{bc - ad}{c^2 + d^2}i
  • pow: z1z2=ez2ln(z1)z_1^{z_2} = e^{z_2 \ln(z_1)}

Unary Transcendental:

  • exp: ez=ea(cosb+isinb)e^z = e^a(\cos b + i \sin b)
  • log: lnz=lnz+iarg(z)\ln z = \ln|z| + i \arg(z)
  • sqrt: z=zeiarg(z)/2\sqrt{z} = \sqrt{|z|} e^{i \arg(z)/2}

Trigonometric:

  • sin: sinz=sinacoshb+icosasinhb\sin z = \sin a \cosh b + i \cos a \sinh b
  • cos: cosz=cosacoshbisinasinhb\cos z = \cos a \cosh b - i \sin a \sinh b
  • tan: tanz=sinz/cosz\tan z = \sin z / \cos z
  • sinh/cosh/tanh: Hyperbolic analogs

Component Extraction:

  • conj: zˉ=abi\bar{z} = a - bi
  • abs: z=a2+b2|z| = \sqrt{a^2 + b^2}
  • arg: arg(z)=atan2(b,a)\arg(z) = \text{atan2}(b, a)
  • real: Re(z)=a\text{Re}(z) = a
  • imag: Im(z)=b\text{Im}(z) = b
  • neg: z=abi-z = -a - bi

Processing Chain:

out=op(lhs_scalez1,rhs_scalez2)+bias\text{out} = \text{op}(\text{lhs\_scale} \cdot z_1, \text{rhs\_scale} \cdot z_2) + \text{bias}

Core Parameters:

ParameterTypeDefaultDescription
modeenum "add"Operation to perform (see below)
rhs_sourceenum "constant"Source for rhs: field or constant
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Mode options:

  • Binary: add, sub, mul, div, pow
  • Unary transcendental: exp, log, sqrt
  • Trigonometric: sin, cos, tan, sinh, cosh, tanh
  • Component: conj, abs, arg, real, imag, neg

Constant RHS:

ParameterTypeDefaultDescription
rhs_constant_redouble 0.0Real part of constant rhs
rhs_constant_imdouble 0.0Imaginary part of constant rhs

Scaling & Bias:

ParameterTypeDefaultDescription
lhs_scale_redouble 1.0Real scale applied to lhs
lhs_scale_imdouble 0.0Imaginary scale applied to lhs
rhs_scale_redouble 1.0Real scale applied to rhs
rhs_scale_imdouble 0.0Imaginary scale applied to rhs
bias_redouble 0.0Real bias added after operation
bias_imdouble 0.0Imaginary bias added after operation

Numerical Guards:

ParameterTypeDefaultRangeDescription
epsilondouble 1e-9≥0Tolerance for log/division guards

Output Configuration:

ParameterTypeDefaultDescription
output_componentenum "real"Component written to real outputs: real, imag, magnitude, phase
phase_wrapenum "none"Phase wrapping: none, signed, unsigned, unit

Phase wrap options:

  • none: No wrapping (raw atan2 output)
  • signed: Wrap to [π,π][-\pi, \pi]
  • unsigned: Wrap to [0,2π][0, 2\pi]
  • unit: Normalize to [0,1][0, 1]
  • Operates elementwise; no boundary handling required
  • For binary operations with rhs_source = "field", both fields must have matching dimensions
  • Division and log operations are protected against zero by epsilon
  • All operations are numerically stable within floating-point precision
  • Division: Protected against division by zero; denominator clamped to epsilon
  • Log: Protected against log(0); magnitude clamped to epsilon
  • Pow: May produce large values for complex exponents; consider clamping outputs
  • Exp: Can overflow for large real parts; typical float range is e88e^{88}
  • Unary operations require single field read
  • Binary operations with constant rhs avoid second field read
  • Trigonometric and transcendental functions use hardware intrinsics when available
  • Component extraction (abs, arg, real, imag) is highly optimized
  • Consider using phase_wrap = "unit" for normalized phase comparisons
-- Complex exponential
ooc.add_complex_math_operator(ctx, z, nil, out, {
mode = "exp"
})
-- Complex multiplication with constant (0.5 + 0.5i)
ooc.add_complex_math_operator(ctx, z, nil, out, {
mode = "mul",
rhs_source = "constant",
rhs_constant_re = 0.5,
rhs_constant_im = 0.5
})
-- Field-vs-field multiplication
ooc.add_complex_math_operator(ctx, a, b, product, {
mode = "mul",
rhs_source = "field"
})
-- Extract magnitude
ooc.add_complex_math_operator(ctx, z, nil, mag, {
mode = "abs"
})
-- Extract phase normalized to [0, 1]
ooc.add_complex_math_operator(ctx, z, nil, phase, {
mode = "arg",
phase_wrap = "unit"
})
-- Extract phase in [-π, π]
ooc.add_complex_math_operator(ctx, z, nil, phase, {
mode = "arg",
phase_wrap = "signed"
})
-- Complex conjugate
ooc.add_complex_math_operator(ctx, z, nil, z_conj, {
mode = "conj"
})
-- Field-vs-field division with epsilon guard
ooc.add_complex_math_operator(ctx, numerator, denominator, quotient, {
mode = "div",
rhs_source = "field",
epsilon = 1e-6
})
-- Complex logarithm
ooc.add_complex_math_operator(ctx, z, nil, log_z, {
mode = "log",
epsilon = 1e-12
})
-- Complex power: z^(0.5) (square root alternative)
ooc.add_complex_math_operator(ctx, z, nil, sqrt_z, {
mode = "pow",
rhs_source = "constant",
rhs_constant_re = 0.5,
rhs_constant_im = 0.0
})
-- Scale and bias: (2+i)*z + (0.1 + 0.2i)
ooc.add_complex_math_operator(ctx, z, nil, out, {
mode = "mul",
rhs_source = "constant",
rhs_constant_re = 1.0,
rhs_constant_im = 0.0,
lhs_scale_re = 2.0,
lhs_scale_im = 1.0,
bias_re = 0.1,
bias_im = 0.2
})

add_chaos_map_operator(ctx, input, out, opts)

Iterate discrete chaotic maps on a real or complex state field. Complex fields use the real and imaginary components as the two phase-space coordinates; real fields seed the imaginary coordinate at zero and write back the updated real component.

add_chaos_map_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

At each application, the operator advances the selected map family

zn+1=F(zn;θ)z_{n+1} = F(z_n; \theta)

and optionally blends the result with the previous state:

zout=(1β)zn+βzn+1z_{\text{out}} = (1 - \beta) z_n + \beta z_{n+1}

where β\beta is blend. iterations_per_step applies the selected map multiple times per simulation step before the final blend.

Standard (Chirikov) Map:

pn+1=pn+Ksin(sxn)xn+1=xn+pn+1\begin{aligned} p_{n+1} &= p_n + K \sin(s x_n) \\ x_{n+1} &= x_n + p_{n+1} \end{aligned}

where KK is the chaos parameter and ss is angle_scale. kick_mode chooses whether the map is applied as kick-drift, drift-kick, or a symmetric half-kick split.

Ikeda Map:

t=ab1+xn2+yn2,zn+1=o+uzneitt = a - \frac{b}{1 + x_n^2 + y_n^2}, \qquad z_{n+1} = o + u z_n e^{i t}

where o=ox+ioyo = o_x + i o_y is the complex offset, uu is the contraction factor, and aa, bb control the nonlinear phase shift.

Exponential Map:

zn+1=eszn+cz_{n+1} = e^{s z_n} + c

with complex scale ss and offset cc.

Quadratic Map:

zn+1=azn2+bzn+cz_{n+1} = a z_n^2 + b z_n + c

Henon Map:

xn+1=ox+gxxn+gyynaxn2yn+1=oy+bxn\begin{aligned} x_{n+1} &= o_x + g_x x_n + g_y y_n - a x_n^2 \\ y_{n+1} &= o_y + b x_n \end{aligned}

Lozi Map:

xn+1=ox+gxxn+gyynaxnyn+1=oy+bxn\begin{aligned} x_{n+1} &= o_x + g_x x_n + g_y y_n - a |x_n| \\ y_{n+1} &= o_y + b x_n \end{aligned}

When lozi_abs_epsilon > 0, the absolute value is softened to xn2+ε2\sqrt{x_n^2 + \varepsilon^2}.

Tinkerbell Map:

xn+1=ox+gx2xn2+gy2yn2+axn+bynyn+1=oy+gxyxnyn+cxn+dyn\begin{aligned} x_{n+1} &= o_x + g_{x^2}x_n^2 + g_{y^2}y_n^2 + a x_n + b y_n \\ y_{n+1} &= o_y + g_{xy}x_n y_n + c x_n + d y_n \end{aligned}

When present, k_field, u_field, a_field, b_field, c_field, and d_field override the corresponding constants per sample. wrap_* constrains the real and imaginary coordinates after each update, and escape_* defines how runaway states are handled once |z| exceeds escape_radius.

Core Parameters:

ParameterTypeDefaultRangeDescription
map_typeenum "standard"see belowChaotic map family
iterations_per_stepinteger 1≥1Map iterations per simulation step
blenddouble 1.0[0, 1]Blend factor (1 = full update)

Map type options: standard, ikeda, exponential, quadratic, henon, lozi, tinkerbell

Standard Map Parameters:

ParameterTypeDefaultRangeDescription
kick_modeenum "kick_drift"see belowKick/drift ordering
kdouble 1.0unboundedChaos parameter KK
angle_scaledouble 1.0unboundedScale factor ss for the sine argument

Kick mode options: kick_drift, drift_kick, symmetric

Ikeda Map Parameters:

ParameterTypeDefaultRangeDescription
ikeda_udouble 0.9[0, 1]Contraction factor
ikeda_adouble 0.4unboundedPhase bias
ikeda_bdouble 6.0unboundedNonlinearity strength
ikeda_offset_redouble 1.0unboundedReal part of offset AA
ikeda_offset_imdouble 0.0unboundedImaginary part of offset AA

Exponential Map Parameters:

ParameterTypeDefaultRangeDescription
exp_scale_redouble 1.0unboundedReal part of scale ss
exp_scale_imdouble 0.0unboundedImaginary part of scale ss
exp_c_redouble 0.0unboundedReal part of constant cc
exp_c_imdouble 0.0unboundedImaginary part of constant cc

Quadratic Map Parameters:

ParameterTypeDefaultRangeDescription
quad_a_redouble 1.0unboundedReal part of quadratic coefficient aa
quad_a_imdouble 0.0unboundedImaginary part of quadratic coefficient aa
quad_b_redouble 0.0unboundedReal part of linear coefficient bb
quad_b_imdouble 0.0unboundedImaginary part of linear coefficient bb
quad_c_redouble 0.0unboundedReal part of constant cc
quad_c_imdouble 0.0unboundedImaginary part of constant cc

Henon Map Parameters:

ParameterTypeDefaultRangeDescription
henon_adouble 1.4unboundedQuadratic coefficient aa
henon_bdouble 0.3unboundedCoupling coefficient bb
henon_x_gaindouble 0.0unboundedLinear X gain gxg_x
henon_y_gaindouble 1.0unboundedCross-coupling gain gyg_y
henon_offset_redouble 1.0unboundedX offset oxo_x
henon_offset_imdouble 0.0unboundedY offset oyo_y

Lozi Map Parameters:

ParameterTypeDefaultRangeDescription
lozi_adouble 1.7unboundedAbsolute-value coefficient aa
lozi_bdouble 0.5unboundedCoupling coefficient bb
lozi_x_gaindouble 0.0unboundedLinear X gain gxg_x
lozi_y_gaindouble 1.0unboundedCross-coupling gain gyg_y
lozi_offset_redouble 1.0unboundedX offset oxo_x
lozi_offset_imdouble 0.0unboundedY offset oyo_y
lozi_abs_epsilondouble 0.0≥0Smooth-absolute epsilon (0 uses exact abs)

Tinkerbell Map Parameters:

ParameterTypeDefaultRangeDescription
tinkerbell_adouble 0.9unboundedLinear X coefficient aa
tinkerbell_bdouble -0.6013unboundedLinear Y coefficient bb in the X update
tinkerbell_cdouble 2.0unboundedLinear X coefficient cc in the Y update
tinkerbell_ddouble 0.5unboundedLinear Y coefficient dd in the Y update
tinkerbell_x2_gaindouble 1.0unboundedQuadratic X gain gx2g_{x^2}
tinkerbell_y2_gaindouble -1.0unboundedQuadratic Y gain gy2g_{y^2}
tinkerbell_xy_gaindouble 2.0unboundedCross term gain gxyg_{xy}
tinkerbell_offset_redouble 0.0unboundedX offset oxo_x
tinkerbell_offset_imdouble 0.0unboundedY offset oyo_y

Per-Element Override Fields:

ParameterTypeDefaultRangeDescription
k_fieldfield handlenilOptional per-sample override for k on the standard map
u_fieldfield handlenilOptional per-sample override for ikeda_u
a_fieldfield handlenilOptional per-sample override for the map-specific a coefficient
b_fieldfield handlenilOptional per-sample override for the map-specific b coefficient
c_fieldfield handlenilOptional per-sample override for the map-specific c coefficient
d_fieldfield handlenilOptional per-sample override for the map-specific d coefficient

Domain & Escape Controls:

ParameterTypeDefaultRangeDescription
wrap_mode_reenum "none"see belowValue-space wrapping mode for the real component
wrap_mode_imenum "none"see belowValue-space wrapping mode for the imaginary component
wrap_min_redouble 0.0unboundedLower real-component wrap bound
wrap_max_redouble unboundedUpper real-component wrap bound
wrap_min_imdouble 0.0unboundedLower imaginary-component wrap bound
wrap_max_imdouble unboundedUpper imaginary-component wrap bound
escape_modeenum "none"see belowAction to take when `
escape_radiusdouble 0.0≥0Escape threshold (<= 0 disables escape handling)
escape_reset_redouble 0.0unboundedReal component used by escape_mode = "reset"
escape_reset_imdouble 0.0unboundedImaginary component used by escape_mode = "reset"

Wrap mode options: none, periodic, clamp, mirror

Escape mode options: none, clamp, reset, nan

  • Operates elementwise; no spatial boundary handling is involved
  • Initial conditions determine the trajectory; nearby states can diverge rapidly
  • Real input fields evolve on the real axis (Im(z) = 0) and write back only the updated real component
  • wrap_* acts on state coordinates, not on spatial sampling
  • escape_radius <= 0 disables escape handling entirely
  • Standard map: Larger k and higher iterations_per_step generally increase chaotic mixing
  • Ikeda map: |ikeda_u| < 1 promotes bounded attractors; larger ikeda_b increases nonlinear folding
  • Quadratic, exponential, and tinkerbell maps: Can diverge quickly; wrap_* and escape_* are useful safety valves
  • Lozi: lozi_abs_epsilon > 0 smooths the non-differentiable corner at the origin
  • blend < 1 damps abrupt state changes but also changes the raw map dynamics
  • iterations_per_step > 1 scales cost linearly with the number of iterations
  • Standard, Henon, and Lozi are comparatively cheap polynomial/elementary maps
  • Ikeda and exponential families require complex exponentials and are the most expensive per iteration
  • Per-element override fields add extra field reads
  • wrap_* and escape_* introduce lightweight branching but can prevent runaway values from poisoning later stages
-- Standard map with K = 1 (near chaotic threshold)
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "standard",
k = 1.0,
kick_mode = "symmetric"
})
-- Standard map with strong chaos
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "standard",
k = 5.0,
kick_mode = "kick_drift"
})
-- Standard map with regular (integrable) motion
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "standard",
k = 0.5, -- below critical value
angle_scale = 1.0
})
-- Ikeda map (classic parameters)
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "ikeda",
ikeda_u = 0.9,
ikeda_a = 0.4,
ikeda_b = 6.0
})
-- Ikeda map with offset
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "ikeda",
ikeda_u = 0.85,
ikeda_a = 0.4,
ikeda_b = 6.0,
ikeda_offset_re = 1.0,
ikeda_offset_im = 0.5
})
-- Quadratic complex map
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "quadratic",
quad_a_re = 1.0,
quad_c_re = -0.8,
quad_c_im = 0.156
})
-- Exponential map (escape-clamped)
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "exponential",
exp_c_re = -0.8,
exp_c_im = 0.156,
escape_mode = "clamp",
escape_radius = 8.0
})
-- Henon map with explicit linear gains
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "henon",
henon_a = 1.4,
henon_b = 0.3,
henon_y_gain = 1.0
})
-- Lozi map with smoothed absolute value near the origin
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "lozi",
lozi_a = 1.7,
lozi_b = 0.5,
lozi_abs_epsilon = 0.01
})
-- Tinkerbell attractor with softened updates
ooc.add_chaos_map_operator(ctx, state, out, {
map_type = "tinkerbell",
tinkerbell_a = 0.9,
tinkerbell_b = -0.6013,
tinkerbell_c = 2.0,
tinkerbell_d = 0.5,
blend = 0.8
})
-- Spatially varying standard-map chaos parameter
ooc.add_chaos_map_operator(ctx, phase_space, next_phase_space, {
map_type = "standard",
k_field = local_k,
angle_scale = 1.0,
iterations_per_step = 2
})

add_hysteretic_operator(ctx, input, output, opts)

Stateful hysteresis operator supporting Schmitt trigger, play model, and Bouc-Wen dynamics. Models memory-dependent nonlinear transformations common in mechanical systems, electronic circuits, and material science. Complex fields are supported and are processed component-wise with independent state on the real and imaginary channels.

add_hysteretic_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

Schmitt Trigger Mode:

Binary output with hysteresis band:

yn={yhighif xn>Thighylowif xn<Tlowyn1otherwisey_n = \begin{cases} y_{\text{high}} & \text{if } x_n > T_{\text{high}} \\ y_{\text{low}} & \text{if } x_n < T_{\text{low}} \\ y_{n-1} & \text{otherwise} \end{cases}

where TlowT_{\text{low}} and ThighT_{\text{high}} define the hysteresis thresholds.

Play Model:

Deadband hysteresis where output only changes when input exceeds accumulated play:

yn={xnrif xn>yn1+rxn+rif xn<yn1ryn1otherwisey_n = \begin{cases} x_n - r & \text{if } x_n > y_{n-1} + r \\ x_n + r & \text{if } x_n < y_{n-1} - r \\ y_{n-1} & \text{otherwise} \end{cases}

where rr is the play_radius.

Bouc-Wen Model:

Differential hysteresis model for structural mechanics:

z˙=Ax˙βx˙zn1zγx˙zn\dot{z} = A\dot{x} - \beta|\dot{x}||z|^{n-1}z - \gamma\dot{x}|z|^n y=αx+(1α)zy = \alpha \cdot x + (1-\alpha) \cdot z

where zz is the internal hysteretic variable and α\alpha, AA, β\beta, γ\gamma, nn are shape parameters. The input is first preprocessed by input_gain, input_bias, and input_mode; complex fields are processed component-wise.

For a complex sample u=ur+iuiu = u_r + i u_i, the operator evaluates the same hysteresis law independently on uru_r and uiu_i, then recombines the result:

H(u)=H(ur)+iH(ui)H(u) = H(u_r) + i H(u_i)

Core Parameters:

ParameterTypeDefaultDescription
modeenum "schmitt"Hysteresis model: schmitt, play, bouc_wen
threshold_modeenum "bounds"Threshold specification: bounds or center_width
input_modeenum "direct"Input preprocessing: direct, abs, squared
input_gaindouble 1.0Gain applied to input before processing
input_biasdouble 0.0Bias added to input
output_gaindouble 1.0Gain applied to output
output_biasdouble 0.0Bias added to output
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Threshold Parameters (bounds mode):

ParameterTypeDefaultDescription
threshold_lowdouble -0.5Lower threshold for hysteresis band
threshold_highdouble 0.5Upper threshold for hysteresis band

Threshold Parameters (center_width mode):

ParameterTypeDefaultDescription
threshold_centerdouble 0.0Center of hysteresis band
threshold_widthdouble 1.0Width of hysteresis band (≥0)

Schmitt Trigger Parameters:

ParameterTypeDefaultDescription
output_lowdouble -1.0Output when below lower threshold
output_highdouble 1.0Output when above upper threshold

Play Model Parameters:

ParameterTypeDefaultDescription
play_radiusdouble 0.0Deadband radius for play hysteresis

Bouc-Wen Parameters:

ParameterTypeDefaultDescription
bw_alphadouble 0.1Ratio of post-yield to pre-yield stiffness
bw_Adouble 1.0Controls tangent stiffness
bw_betadouble 0.5Shape parameter (softening/hardening)
bw_gammadouble 0.5Shape parameter (pinching)
bw_ndouble 2.0Exponent controlling sharpness
bw_z_clampdouble 0.0Clamp on internal variable z (0 = no clamp)
bw_xdot_clampdouble 0.0Clamp on velocity estimate (0 = no clamp)

Smoothing & Rate Limiting:

ParameterTypeDefaultDescription
smoothdouble 0.0Exponential smoothing factor [0, 1]
rate_limitdouble 0.0Maximum
state_mindouble -1e6Minimum internal state value
state_maxdouble 1e6Maximum internal state value

Initialization:

ParameterTypeDefaultDescription
initialize_from_inputboolean trueInitialize state from first input sample
initial_outputdouble 0.0Initial output if not from input
initial_inputdouble 0.0Initial input reference
initial_zdouble 0.0Initial Bouc-Wen internal variable
  • Operates elementwise; no spatial boundary handling
  • Internal state is maintained per-element across timesteps
  • Complex fields keep separate hysteresis state for the real and imaginary channels
  • Use initialize_from_input = true for automatic state initialization
  • For deterministic behavior, set explicit initial values
  • Schmitt mode: Unconditionally stable; discrete state transitions
  • Play mode: Unconditionally stable; bounded by input range
  • Bouc-Wen mode: Stability depends on parameter choices:
    • β+γ>0\beta + \gamma > 0 ensures bounded dissipation
    • Large nn values can cause stiff dynamics; reduce timestep accordingly
    • Use bw_z_clamp to prevent runaway internal states
  • Maintains per-element internal state; memory usage scales with field size
  • Schmitt and play modes are computationally lightweight
  • Bouc-Wen requires velocity estimation (finite difference) and is more expensive
  • Complex fields approximately double the scalar state/work because the channels are evolved independently
  • rate_limit > 0 adds conditional logic per element
-- Schmitt trigger with ±0.3 thresholds
ooc.add_hysteretic_operator(ctx, input, output, {
mode = "schmitt",
threshold_low = -0.3,
threshold_high = 0.3,
output_low = 0.0,
output_high = 1.0
})
-- Center-width threshold specification
ooc.add_hysteretic_operator(ctx, input, output, {
mode = "schmitt",
threshold_mode = "center_width",
threshold_center = 0.5,
threshold_width = 0.2 -- thresholds at 0.4 and 0.6
})
-- Play model with deadband
ooc.add_hysteretic_operator(ctx, position, force, {
mode = "play",
play_radius = 0.1,
input_gain = 100.0 -- spring constant
})
-- Bouc-Wen for structural hysteresis
ooc.add_hysteretic_operator(ctx, displacement, restoring_force, {
mode = "bouc_wen",
bw_alpha = 0.05,
bw_A = 1.0,
bw_beta = 0.5,
bw_gamma = 0.5,
bw_n = 2.0
})
-- Smooth Schmitt with rate limiting (debouncing)
ooc.add_hysteretic_operator(ctx, noisy_signal, clean_signal, {
mode = "schmitt",
threshold_low = -0.1,
threshold_high = 0.1,
smooth = 0.9,
rate_limit = 10.0 -- max 10 units/second change rate
})
-- Magnitude-based hysteresis (rectified input)
ooc.add_hysteretic_operator(ctx, ac_signal, envelope, {
mode = "play",
input_mode = "abs",
play_radius = 0.05,
smooth = 0.8
})
-- Complex I/Q hysteresis (applied independently to real and imaginary parts)
ooc.add_hysteretic_operator(ctx, iq_signal, iq_shaped, {
mode = "schmitt",
threshold_low = -0.2,
threshold_high = 0.2,
output_low = -1.0,
output_high = 1.0
})

add_neural_infer_operator(ctx, input, out, opts)

Run an inference-only learned operator on a field while carrying explicit metadata about determinism, device placement, precision, and accepted tensor shape.

Experimental: Model adapters, execution contracts, and fallback behavior may evolve as the neural runtime is refined.

add_neural_infer_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

Conceptually, the operator applies a learned map:

y^=Fθ(x~)\hat{y} = F_{\theta}(\tilde{x})

with optional affine input/output transforms

x~={sinx+binwhen input normalization is enabledxotherwise\tilde{x} = \begin{cases} s_{\text{in}} x + b_{\text{in}} & \text{when input normalization is enabled} \\ x & \text{otherwise} \end{cases}

and writes

y={Δty^when dt scaling is enabledy^otherwisey = \begin{cases} \Delta t \cdot \hat{y} & \text{when dt scaling is enabled} \\ \hat{y} & \text{otherwise} \end{cases}

Here, normalize_input controls the affine preprocessing and scale_by_dt controls the optional Δt\Delta t writeback scaling.

If no backend model or custom inference callback is available, the current runtime falls back to an affine passthrough using the configured normalization/denormalization settings.

Inference Contract:

ParameterTypeDefaultDescription
model_idstring "model"Model identifier resolved by the backend adapter
determinism_policyenum "inherit"inherit, strict, best_effort, off
device_requirementenum "any"any, cpu_only, accelerator_preferred, accelerator_required
precision_modeenum "default"default, fp32, fp64, mixed, fp16, bf16

Shape Constraints:

ParameterTypeDefaultDescription
min_rankinteger 1Minimum accepted input rank
max_rankinteger 0Maximum accepted input rank (0 disables the upper bound)
channel_axisinteger 255Channel axis (255 = auto)
channels_lastboolean trueAuto-axis policy when channel_axis = 255
allow_complex_inputboolean trueAllow complex-valued input tensors/fields

Normalization & Writeback:

ParameterTypeDefaultDescription
normalize_inputboolean falseApply affine preprocessing before inference
input_scaledouble 1.0Input normalization scale
input_biasdouble 0.0Input normalization bias
output_scaledouble 1.0Output denormalization scale
output_biasdouble 0.0Output denormalization bias
accumulateboolean falseAdd prediction into the output field
scale_by_dtboolean trueScale writes by substep dt
  • Input and output fields must have identical shape and storage type
  • Rank and channel layout must satisfy the configured shape constraints
  • Complex inputs are permitted only when allow_complex_input = true
  • Numerical stability depends on the learned model rather than a fixed analytic formula
  • strict determinism may reduce backend choices but improves reproducibility
  • scale_by_dt = true is appropriate when the model predicts a rate-like increment rather than a full state
  • Allocates a temporary prediction buffer each step
  • Backend selection and precision can materially affect throughput
  • The operator is nonlocal from the scheduler’s perspective even though the I/O shape matches pointwise field updates
-- Deterministic surrogate inference
ooc.add_neural_infer_operator(ctx, state, prediction, {
model_id = "burgers_surrogate_v1",
determinism_policy = "strict",
precision_mode = "fp32"
})
-- Explicit tensor layout contract
ooc.add_neural_infer_operator(ctx, input, output, {
model_id = "vision_block",
min_rank = 3,
max_rank = 3,
channel_axis = 0,
channels_last = false
})
-- Accumulate model output as an RHS term
ooc.add_neural_infer_operator(ctx, state, rhs, {
model_id = "residual_term",
accumulate = true,
scale_by_dt = true
})

add_neural_hybrid_operator(ctx, input, out, opts)

Combine an analytic baseline with a learned residual prediction. This is useful when you want the model to correct, not replace, a known signal path.

Experimental: Model adapters, execution contracts, and blend semantics may evolve as the neural runtime is refined.

add_neural_hybrid_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

The operator forms a hybrid write from the input field xx and a model prediction r^\hat{r}:

y=gax+grr^y = g_a x + g_r \hat{r}

where:

  • gag_a is analytic_gain
  • grg_r is residual_gain
  • r^=Fθ(x~)\hat{r} = F_{\theta}(\tilde{x})

As with add_neural_infer_operator, the runtime can normalize inputs before inference and apply output scaling/bias afterward. If no model backend is available, the residual path currently falls back to an affine-transformed copy of the input.

Hybrid Weights:

ParameterTypeDefaultDescription
analytic_gaindouble 1.0Scale applied to the direct analytic/input contribution
residual_gaindouble 1.0Scale applied to the learned residual contribution

Inference Contract:

ParameterTypeDefaultDescription
model_idstring "model"Residual model identifier
determinism_policyenum "inherit"inherit, strict, best_effort, off
device_requirementenum "any"any, cpu_only, accelerator_preferred, accelerator_required
precision_modeenum "default"default, fp32, fp64, mixed, fp16, bf16

Shape Constraints & Normalization:

ParameterTypeDefaultDescription
min_rankinteger 1Minimum accepted input rank
max_rankinteger 0Maximum accepted input rank (0 disables the upper bound)
channel_axisinteger 255Channel axis (255 = auto)
channels_lastboolean trueAuto-axis policy when channel_axis = 255
allow_complex_inputboolean trueAllow complex-valued input tensors/fields
normalize_inputboolean falseApply affine preprocessing before residual inference
input_scaledouble 1.0Input normalization scale
input_biasdouble 0.0Input normalization bias
output_scaledouble 1.0Output denormalization scale on the residual path
output_biasdouble 0.0Output denormalization bias on the residual path
accumulateboolean falseAdd the hybrid output into the destination field
scale_by_dtboolean trueScale writes by substep dt
  • Input and output fields must match in shape and scalar/complex storage
  • The same shape-contract rules as Neural Infer apply
  • Complex outputs combine the analytic and residual paths component-wise
  • analytic_gain and residual_gain directly control how strongly the learned correction can perturb the baseline
  • Start with small residual_gain values when introducing a new surrogate into an existing simulation
  • scale_by_dt = true is appropriate when the hybrid output represents an increment rather than a full-state overwrite
  • Same backend and buffer costs as Neural Infer, plus the final blend with the input field
  • Useful for residual-learning workflows where the analytic path remains interpretable
  • Determinism, device, and precision choices are exposed explicitly so the runtime contract stays inspectable
-- Residual correction on top of the current state
ooc.add_neural_hybrid_operator(ctx, state, next_state, {
model_id = "navier_stokes_residual_v2",
analytic_gain = 1.0,
residual_gain = 0.2
})
-- CPU-only deterministic hybrid for reproducible tests
ooc.add_neural_hybrid_operator(ctx, u, out, {
model_id = "test_residual",
determinism_policy = "strict",
device_requirement = "cpu_only",
residual_gain = 0.1
})
-- Accumulate learned correction into an RHS buffer
ooc.add_neural_hybrid_operator(ctx, state, rhs, {
model_id = "forcing_residual",
analytic_gain = 0.0,
residual_gain = 1.0,
accumulate = true,
scale_by_dt = true
})