Skip to content

Binaryzation ​

Contract ​

BinaryzationFunction<V> maps one linear polynomial p to a binary result. Its current contract is positive-part binarization with the positive gap ε (the constructor's tolerance parameter):

y=Bin(p)={1,p≥εundefined,0<p<ε0,p≤0

The input need not be a binary variable; it may be any LinearPolynomial<V> with V : RealNumber<V> & NumberField<V>.

Definition and truth table ​

py
p≥ε1
0<p<εnull
p≤00

The result is a binary variable, not a numeric copy of p.

Boundary, tolerance, and Undefined ​

evaluate() classifies p with the relation p > 0 and the gap ε: it returns 1 for p≥ε, 0 for p≤0, and null inside the gap; a missing polynomial value also returns null.

The solver registration uses the tolerance parameter, defaulting to NONZERO_TOLERANCE = 1e-10, as ε. If a is the result variable and M is the selected Big-M value, the essential indicator constraints are:

p−Ma≤0,p−M′a≥ε−M′.

Consequently, a=0 requires p≤0, while a=1 requires p≥ε. The open interval 0<p<ε is a solver gap: evaluate() returns null there, and the linearized model has no valid branch either.

When bigM is omitted, the implementation derives it from the polynomial's finite range and falls back to BIG_M_DEFAULT = 1e6 if necessary.

Current API ​

Kotlin ​

kotlin
BinaryzationFunction(
    polynomial: LinearPolynomial<V>,
    converter: IntoValue<V>,
    bigM: V? = null,
    name: String = "bin",
    displayName: String? = null,
    tolerance: V? = null
)

The companion invoke takes the same arguments except that name is required. converter is required; old scalar constructors are not part of the current API.

Rust ​

Source: binaryzation.rs

Rust exposes BinaryzationFunction::new(id, name, input, threshold, big_m, method) plus named/automatic factories. The convenience constructors are with_big_m/named_big_m/auto_big_m (strict input > threshold, with threshold zero for the Kotlin-compatible positive test) and with_threshold/named_threshold/auto_threshold (inclusive input >= threshold). BinaryzationMethod is one of BigM, Threshold, Indicator, or SOS1; the last two currently use the mechanism-layer Big-M equivalent.

rust
BinaryzationFunction::new(
    id: u64,
    name: &str,
    input: Linear<V>,
    threshold: V,
    big_m: V,
    method: BinaryzationMethod,
) -> Self
BinaryzationFunction::named_big_m(name: impl AsRef<str>, input: Linear<V>, big_m: V) -> Self
BinaryzationFunction::named_threshold(name: impl AsRef<str>, input: Linear<V>, threshold: V) -> Self

Solver mathematical model ​

For binary result a and positive tolerance ε, the rows passed to the solver are

p−Ma≤0,p−M′a≥ε−M′.

Thus a=0⇒p≤0 and a=1⇒p≥ε. Kotlin creates only name_bin; Rust uses the same two-branch idea, with its constructor-selected threshold and Big-M/method. The interval 0<p<ε has no feasible branch.

evaluate() versus the solver model ​

The evaluator and the solver model agree on the gap: both treat 0<p<ε as out of domain (evaluate() returns null, and the registered rows leave no feasible branch). If the model can produce values in that interval, the function reports no value instead of a misleading 1.

Minimal current example ​

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.BinaryzationFunction
import fuookami.ospf.kotlin.core.variable.RealVar
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

fun main() {
    val x = RealVar("x")
    val xPoly = LinearPolynomial(
        monomials = listOf(LinearMonomial(Flt64.one, x)),
        constant = Flt64.zero
    )
    val function = BinaryzationFunction(
        polynomial = xPoly,
        converter = IntoValue.Identity,
        name = "bin"
    )

    check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64(2.0))) == Flt64.one)
    check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero)) == Flt64.zero)
    check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64(-1.0))) == Flt64.zero)
}
rust
use ospf_rust_core::symbol::flatten::Linear;
use ospf_rust_core::symbol::function::BinaryzationFunction;
use ospf_rust_core::symbol::FunctionSymbol;
use ospf_rust_core::token::VecTokenList;

let input = Linear::new(vec![], 2.0);
let function = BinaryzationFunction::named_big_m("bin", input, 10.0);
let value = <BinaryzationFunction as FunctionSymbol>::calculate_value(
    &function,
    &VecTokenList::<f64>::new(),
    false,
);
assert_eq!(value, Some(1.0));

Source and core tests ​