Logical NOT
Contract
NotFunction<V> accepts one linear polynomial and exposes a binary result. The result is 1 exactly when the polynomial is zero, and 0 when it is nonzero. The API is generic over V : RealNumber<V> & NumberField<V>.
The operation is the inverse of the current nonzero indicator. It is not a Boolean negation that assumes a pre-existing binary input variable.
Definition and truth table
For a linear polynomial
| zero | 1 |
| nonzero | 0 |
Boundary, tolerance, and Undefined
evaluate() compares the evaluated polynomial with exact zero and returns null if the input is missing. It has no Undefined return branch.
The solver's nonzero indicator uses tolerance indicatorVar = 0 represents indicatorVar = 1 requires
The current defaults are NONZERO_TOLERANCE = 1e-10 and STRICT_BOUNDARY = NONZERO_TOLERANCE * 16 + 16 * 2^-52. Omitted bigM is inferred from the polynomial's finite range, with BIG_M_DEFAULT = 1e6 as the fallback.
Current API
Kotlin
NotFunction(
polynomial: LinearPolynomial<V>,
converter: IntoValue<V>,
bigM: V? = null,
tolerance: V? = null,
strictBoundary: V? = null,
name: String = "not",
displayName: String? = null
)The companion invoke accepts polynomial, converter, bigM, name, and displayName; use the primary constructor to set tolerance or strictBoundary explicitly.
Rust
Rust exposes NotFunction in the same module as OrFunction and XorFunction:
NotFunction::new(id: u64, name: &str, polynomial: Linear<V>) -> NotFunction<V>The result, nonzero indicator, and side helper are available through result_variable(), indicator_variable(), and side_variable(). Rust uses the shared nonzero-indicator defaults and does not expose Kotlin's per-instance tolerance or strictBoundary parameters; evaluate still treats an exact zero as true for NOT.
Solver mathematical model
For name, the implementation creates:
name_not_nz: the nonzero indicator (a);name_not_side: the sign-side helper used by the nonzero test;name_not: the binary result (y).
For the general polynomial path all three are in helperVariables. When the polynomial is a single binary variable, both implementations take the direct binary path instead: only name_not is registered, and the only registered row is the complement
After expanding those implications into linear inequalities, registration appends
The public resultPolynomial is the unit-coefficient polynomial of name_not; constraints are registered on AbstractLinearMechanismModel.
Rust likewise registers a nonzero indicator and the complement equality
evaluate() versus the solver model
The evaluator treats every exact nonzero value as false for NOT, even when its magnitude is below strictBoundary. The solver has a zero band, a strict nonzero band, and an unclassified gap. Do not use a value from the gap as a solver input without changing the boundaries or the model's value lattice.
Examples and tests
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.NotFunction
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 = NotFunction(
polynomial = xPoly,
converter = IntoValue.Identity,
name = "not"
)
check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero)) == Flt64.one)
check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64(3.0))) == Flt64.zero)
}use ospf_rust_core::symbol::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::NotFunction;
let input = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let not = NotFunction::new(1, "not", input);
let _result = not.result_variable();Source and core tests:
- Implementation:
And.kt - Core generic registration test:
FunctionSymbolGenericRegistrationTest.kt - Complete example:
NotTest.kt
Rust source and regression coverage: and.rs and gurobi_linear_function_kotlin_parity.rs.