Binaryzation
Contract
BinaryzationFunction<V> maps one linear polynomial tolerance parameter):
The input need not be a binary variable; it may be any LinearPolynomial<V> with V : RealNumber<V> & NumberField<V>.
Definition and truth table
| 1 | |
null | |
| 0 |
The result is a binary variable, not a numeric copy of
Boundary, tolerance, and Undefined
evaluate() classifies p > 0 and the gap 1 for 0 for 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
Consequently, 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
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.
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) -> SelfSolver mathematical model
For binary result
Thus name_bin; Rust uses the same two-branch idea, with its constructor-selected threshold and Big-M/method. The interval
evaluate() versus the solver model
The evaluator and the solver model agree on the gap: both treat 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
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)
}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
- Implementation:
Binaryzation.kt - Core regression test:
FunctionSymbolRegressionTest.kt - Core result-polynomial contract test:
LegacyResultPolynomialContractTest.kt - Complete example:
BinTest.kt - Rust implementation:
binaryzation.rs - Rust core coverage:
p0_evaluation_tests.rs