Sigmoid
SigmoidFunction is a binary step/condition indicator, despite its name. It is not the continuous logistic function
WARNING
The current implementation uses the shared three-valued discrete-condition classifier and returns null in its boundary gap; it does not approximate a smooth sigmoid curve.
Contract
- Input: condition
LinearPolynomial<V>interpreted withrelation(defaultComparison.GT) against zero. - Output:
resultPolynomialcontaining the binary indicator whose name appends_sig_indtoname. - True/false values are one/zero; an undefined gap or missing input evaluates to
null. strictBoundaryanddeltadefine the branch separation and discrete conversion;toleranceis used when strictBoundary is omitted.- Solver registration requires finite, ordered condition bounds. Legacy
bigMcannot replace those bounds.
Definition and mathematical model
For the default GT relation and gap
The corresponding reversed inequalities are used for LT, and the analogous matrix is used for GE and LE.
Solver mathematical model
For normalized condition
These are the only nonconstant rows: SigmoidFunction (the relation-step entry) uses the same relation-indicator model. Rust LogisticFunction::new instead registers a sampled logistic piecewise-linear model and must not be interpreted as these two rows.
Current API
Kotlin
Source: Sigmoid.kt (SigmoidFunction)
The public factory and constructor expose the same condition, boundary, relation, bound, and naming parameters; use conditionBounds (or the bounds alias) for the finite solver range.
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.ConditionBounds
import fuookami.ospf.kotlin.core.symbol.function.SigmoidFunction
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.monomial.LinearMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.LinearPolynomial
import fuookami.ospf.kotlin.core.variable.RealVar
val x = RealVar("x")
val condition = LinearPolynomial(
listOf(LinearMonomial(Flt64.one, x)), Flt64.zero
)
val sigmoid = SigmoidFunction(
condition = condition,
converter = IntoValue.Identity,
strictBoundary = Flt64(0.1),
conditionBounds = ConditionBounds(Flt64(-10.0), Flt64(10.0)),
name = "sigmoid"
)
val value = sigmoid.evaluate(mapOf<Symbol, Flt64>(x to Flt64.one))
check(value == Flt64.one)Rust
The Kotlin page's binary relation-step semantics map to Rust SigmoidFunction, not to Rust's continuous PWL LogisticFunction symbol:
SigmoidFunction::from_parts(
condition: Linear<V>,
relation: ConditionRelation,
strict_boundary: V,
bounds: ConditionBounds<V>,
) -> Result<SigmoidFunction<V>>
LogisticFunction::new(
id: u64,
name: &str,
input: Linear<V>,
) -> LogisticFunction<V>SigmoidFunction is the closest Rust API: it classifies a relation with True/False/Undefined and exposes a binary result_variable(). It is also available through LogisticFunction::step/relation and the aliases SigmoidRelationFunction and ConditionalLogisticFunction. Rust's LogisticFunction::new instead builds a sampled continuous logistic PWL function; its direct evaluator is
Evaluate versus solver
Direct evaluate calls classify and maps True/False/Undefined to one/zero/null. Solver registration runs the precheck, normalizes the condition, folds constants, registers the indicator, and adds the shared relation constraints. A false direct branch does not bypass solver-time range validation.
Boundaries, tolerance, and Undefined
Missing condition values or non-finite values fail classification and produce null from evaluate. The gap is relation-dependent; for default GT it is strictBoundary/delta, reversed/sentinel bounds, and invalid Big-M values return a failed Result during registration.
Examples and tests
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.ConditionBounds
import fuookami.ospf.kotlin.core.symbol.function.SigmoidFunction
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.monomial.LinearMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.LinearPolynomial
import fuookami.ospf.kotlin.core.variable.RealVar
val x = RealVar("x")
val condition = LinearPolynomial(
listOf(LinearMonomial(Flt64.one, x)), Flt64.zero
)
val sigmoid = SigmoidFunction(
condition = condition,
converter = IntoValue.Identity,
strictBoundary = Flt64(0.1),
conditionBounds = ConditionBounds(Flt64(-10.0), Flt64(10.0)),
name = "sigmoid"
)
val value = sigmoid.evaluate(mapOf<Symbol, Flt64>(x to Flt64.one))
check(value == Flt64.one)use ospf_rust_core::symbol::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::{
ConditionBounds, ConditionRelation, LogisticFunction, SigmoidFunction,
};
let condition = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let step = SigmoidFunction::from_parts(
condition.clone(),
ConditionRelation::Greater,
0.1_f64,
ConditionBounds { lower: -10.0, upper: 10.0 },
)
.unwrap();
assert_eq!(step.evaluate(&1.0).unwrap(), Some(1.0));
let smooth = LogisticFunction::new(2, "sigmoid", condition);
let _smooth_result = smooth.result_variable();- Core regression:
ConditionalFunctionRegressionTest.kt - Core registration test:
FunctionSymbolConditionalGenericRegistrationTest.kt - Example directory (no dedicated sigmoid file): linear_function
Rust sources: sigmoid.rs, conditional.rs, and conditional_function_solver_regression.rs.