kumulant

schema.expr

The serializable expression AST behind the lambda-bound operations in com.eignex.kumulant.schema. When an operation needs a projection or a predicate that must travel on the wire rather than as a live Kotlin lambda, it carries one of these trees instead. The whole tree is serializable, so a filter or a transform round-trips as data and rebuilds an identical closure on the far side.

The three trees

There are three sealed hierarchies, one per result shape. ScalarExpr evaluates to a single number from an update's value, its paired second component, and the current vector. VectorExpr evaluates to a whole vector and backs the per-feature projection and scaling operations. BoolExpr evaluates to a boolean and drives filter predicates and the guards on conditional nodes. Each hierarchy is sealed, so serialization is closed and exhaustive: every node carries a discriminator and decoding can never land on an unknown shape.

Leaves and operators

The scalar leaves are the update value X, the paired second component Y, a vector coordinate V, and a literal Const. Arithmetic composes them with the ordinary operators and with dedicated nodes for the transcendental functions: powers, logs, exponentials, square roots, and absolute value. Comparisons between scalars produce a BoolExpr, and the boolean combinators join several predicates into one.

Sugar nodes

Common shapes have dedicated nodes so a serialized tree stays readable rather than ballooning into nested arithmetic. Standardizing, min-max scaling, centering, and range tests each have their own node, as does the multi-branch Switch and the conditional IfExpr. Vector projection has nodes for dot products, element selection, single-coordinate access, and a fold whose operator picks sum, min, max, or mean.

Anything that cannot be expressed as one of these nodes stays a live-only lambda on the com.eignex.kumulant.core.Stat side and is not wire-expressible. Reach for the lambda overloads of the filter and transform operations in com.eignex.kumulant.operation when that happens.

Types

Link copied to clipboard
@Serializable
sealed interface BoolExpr

Wire-serialisable AST for boolean predicates over the same input environment as ScalarExpr. Consumed by every spec-side filter operator and by IfExpr's condition slot.

Link copied to clipboard
@Serializable
@SerialName(value = "Center")
data object Center : ScalarExpr

Reads the center field of the feedback primary's snapshot. Requires the primary's result to implement HasCenterScale; raises IllegalStateException when evaluated without a primary, or when the primary's result does not expose a center.

Link copied to clipboard
@Serializable
@SerialName(value = "Const")
data class Const(val v: Double) : ScalarExpr

Wire spec for a constant scalar.

Link copied to clipboard
@Serializable
@SerialName(value = "High")
data object High : ScalarExpr

Reads the max field of the feedback primary's snapshot. Requires the primary's result to implement HasMinMax; raises IllegalStateException when evaluated without such a primary.

Link copied to clipboard
@Serializable
@SerialName(value = "IfExpr")
data class IfExpr(val cond: BoolExpr, val then: ScalarExpr, val otherwise: ScalarExpr) : ScalarExpr

Wire spec for a ternary if (cond) then else otherwise.

Link copied to clipboard
@Serializable
@SerialName(value = "In")
data class In(val of: ScalarExpr, val values: List<Double>) : BoolExpr

Membership test: of in values (exact equality). Use it to flatten chains of Eq predicates joined by Or; e.g. VIndex In listOf(0.0, 3.0).

Link copied to clipboard
@Serializable
@SerialName(value = "Low")
data object Low : ScalarExpr

Reads the min field of the feedback primary's snapshot. Requires the primary's result to implement HasMinMax; raises IllegalStateException when evaluated without such a primary.

Link copied to clipboard
@Serializable
@SerialName(value = "MinMax")
data class MinMax(val targetLow: Double = 0.0, val targetHigh: Double = 1.0) : ScalarExpr

Min-max projection mapping X from [primary.min, primary.max] to [targetLow, targetHigh], emitting targetLow while the running range is still degenerate. Reusable AST sugar for the min-max-scaler pattern; requires a HasMinMax primary.

Link copied to clipboard
@Serializable
sealed interface ScalarExpr

Wire-serialisable AST for scalar expressions over the per-update input environment. The library uses these wherever a stat needs to apply a caller-supplied projection / weight / threshold expression that has to round-trip on the wire; weightBy, transform, the per-bin scaler projections, the WithFeedback op, the loss / pinball / quantile configurations.

Link copied to clipboard
@Serializable
@SerialName(value = "Scale")
data object Scale : ScalarExpr

Reads the scale field of the feedback primary's snapshot. Requires the primary's result to implement HasCenterScale; raises IllegalStateException when evaluated without a primary, or when the primary's result does not expose a scale.

Link copied to clipboard
@Serializable
@SerialName(value = "Standardize")
data object Standardize : ScalarExpr

Z-score projection: (X - Center) / Scale, emitting 0 when Scale is still zero. Reusable AST sugar for the standard-scaler pattern; requires a HasCenterScale primary.

Link copied to clipboard
@Serializable
@SerialName(value = "Switch")
data class Switch(val on: ScalarExpr, val cases: List<SwitchCase>, val otherwise: ScalarExpr) : ScalarExpr

Multi-way branch on a scalar key. Replaces nested IfExpr cascades. The first case whose SwitchCase.value equals on.eval(...) exactly wins; if none match, otherwise is returned.

Link copied to clipboard
@Serializable
@SerialName(value = "SwitchCase")
data class SwitchCase(val value: Double, val then: ScalarExpr)

One case of a Switch expression: when on evaluates to value (exact equality), the result is then. Exact double equality is fine for integer-valued keys like VIndex; not recommended for general continuous comparisons.

Link copied to clipboard
@Serializable
@SerialName(value = "V")
data class V(val index: Int) : ScalarExpr

v[index] - out-of-bounds throws at eval time.

Link copied to clipboard
@Serializable
sealed interface VectorExpr

Wire-serialisable AST for vector-valued expressions over the same input environment as ScalarExpr / BoolExpr. Used by the spec-side transformVector(VectorExpr) operator when the output is a fresh vector rather than a per-element transform.

Link copied to clipboard
@Serializable
enum VFoldOp : Enum<VFoldOp>

Reduction over the entire vector input. Distinct from element-level arithmetic (Add, Mul) - this collapses a DoubleArray of arbitrary length to a single scalar via the chosen operation.

Link copied to clipboard
@Serializable
@SerialName(value = "VIndex")
data object VIndex : ScalarExpr

In element-wise feedback contexts (vector / regression / paired), returns the coordinate index of the currently evaluating element as a Double. Outside such contexts (primary is not an IndexedResult), raises IllegalStateException.

Link copied to clipboard
@Serializable
@SerialName(value = "X")
data object X : ScalarExpr

Refers to the primary scalar input x.

Link copied to clipboard
@Serializable
@SerialName(value = "Y")
data object Y : ScalarExpr

Refers to the secondary scalar input y (paired stats only).

Functions

Link copied to clipboard
infix fun BoolExpr.and(rhs: BoolExpr): BoolExpr

Short-circuiting conjunction.

Link copied to clipboard
operator fun ScalarExpr.div(rhs: ScalarExpr): ScalarExpr

Build Div of two expressions.

operator fun ScalarExpr.div(rhs: Double): ScalarExpr

Divide this expression by a literal rhs.

operator fun Double.div(rhs: ScalarExpr): ScalarExpr

Divide a literal receiver by an expression.

Link copied to clipboard
infix fun ScalarExpr.eq(rhs: ScalarExpr): BoolExpr

Exact equality (no tolerance).

infix fun ScalarExpr.eq(rhs: Double): BoolExpr

Exact equality against a literal (no tolerance).

Link copied to clipboard
infix fun ScalarExpr.ge(rhs: ScalarExpr): BoolExpr

Greater-or-equal comparison.

infix fun ScalarExpr.ge(rhs: Double): BoolExpr

Greater-or-equal against a literal.

Link copied to clipboard
infix fun ScalarExpr.gt(rhs: ScalarExpr): BoolExpr

Strictly-greater-than comparison.

infix fun ScalarExpr.gt(rhs: Double): BoolExpr

Strictly-greater-than against a literal.

Link copied to clipboard
infix fun ScalarExpr.le(rhs: ScalarExpr): BoolExpr

Less-or-equal comparison.

infix fun ScalarExpr.le(rhs: Double): BoolExpr

Less-or-equal against a literal.

Link copied to clipboard
infix fun ScalarExpr.lt(rhs: ScalarExpr): BoolExpr

Strictly-less-than comparison.

infix fun ScalarExpr.lt(rhs: Double): BoolExpr

Strictly-less-than against a literal.

Link copied to clipboard

Build Sub of two expressions.

operator fun ScalarExpr.minus(rhs: Double): ScalarExpr

Subtract a literal rhs from this expression.

operator fun Double.minus(rhs: ScalarExpr): ScalarExpr

Subtract an expression from a literal receiver.

Link copied to clipboard
operator fun BoolExpr.not(): BoolExpr

Logical negation.

Link copied to clipboard
infix fun BoolExpr.or(rhs: BoolExpr): BoolExpr

Short-circuiting disjunction.

Link copied to clipboard
operator fun ScalarExpr.plus(rhs: ScalarExpr): ScalarExpr

Build Add of two expressions.

operator fun ScalarExpr.plus(rhs: Double): ScalarExpr

Add a literal rhs to this expression.

operator fun Double.plus(rhs: ScalarExpr): ScalarExpr

Add an expression to a literal receiver.

Link copied to clipboard

Build Mul of two expressions.

operator fun ScalarExpr.times(rhs: Double): ScalarExpr

Multiply this expression by a literal rhs.

operator fun Double.times(rhs: ScalarExpr): ScalarExpr

Multiply a literal receiver by an expression.

Link copied to clipboard

Unary minus: wraps in Neg.