Satisfied Amount
SatisfiedAmountFunction counts how many input linear inequalities are satisfied, or tests whether that count reaches a threshold.
Contract
- Input:
List<LinearInequality<V>>, with a caller-suppliedepsilon. - With
amount = null, outputresultis the raw count. - With
amountset, output is binary: one iff the count is at leastamount. - Direct evaluation recognizes LE/LT/GE/GT/EQ/NE; solver registration currently supports only LE/GE/EQ and fails for LT/GT/NE.
- Each inequality uses a binary satisfaction flag; EQ also uses a side flag.
- Big-M defaults from each inequality's difference polynomial when possible.
Definition and mathematical model
For inequality
The result is
The threshold mode is “at least”, not exact-count equality.
Solver mathematical model
Kotlin creates EQ). It exposes
When amount = k, the only additional solver row is
The solver result remains the count with_amount_range adds hard lower/upper bounds on that count.
Current API
Kotlin
Source: SatisfiedAmount.kt (SatisfiedAmountFunction)
The companion factory has the same public arguments. Use amount = null to retain the count rather than the threshold indicator.
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.SatisfiedAmountFunction
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.inequality.Comparison
import fuookami.ospf.kotlin.math.symbol.inequality.LinearInequality
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 xPoly = LinearPolynomial(listOf(LinearMonomial(Flt64.one, x)), Flt64.zero)
val one = LinearPolynomial<Flt64>(emptyList(), Flt64.one)
val inequality = LinearInequality(xPoly, one, Comparison.LE, "x_le_1")
val satisfied = SatisfiedAmountFunction(
inequalities = listOf(inequality),
amount = UInt64.one,
epsilon = Flt64(1e-6),
bigM = Flt64(10.0),
converter = IntoValue.Identity,
name = "satisfied"
)
val value = satisfied.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero))
check(value == Flt64.one)Rust
Rust currently has no direct counterpart that accepts Vec<LinearInequality<V>>, an epsilon, and a Kotlin-style threshold result. SatisfiedAmountFunction counts already-created binary indicator variables:
SatisfiedAmountFunction::new(
id: u64,
name: &str,
indicators: Vec<BinaryVariableItem>,
) -> Self
pub fn with_amount_range(
mut self,
lower: Option<usize>,
upper: Option<usize>,
) -> SelfThe convenience constructors are any, all, at_least, not_all, and numerable. result_variable() is a continuous count equal to the sum of the indicators; with_amount_range adds hard count bounds and does not create Kotlin's separate binary threshold result. Build each inequality indicator separately (for example with Rust InequalityFunction) and register both symbols in the model.
Evaluate versus solver
Direct evaluation applies epsilon to each comparison and can count all six comparison kinds. Solver registration uses the supported encodings and returns a failed Result for LT, GT, or NE before model write. A non-positive or insufficient Big-M, missing symbols, or a difference range that cannot be inferred can make registration fail even when direct counting is defined.
Boundaries, tolerance, and Undefined
Missing input values return null from direct evaluate. amount is converted to an Int-sized count by the current implementation; callers should keep it within the input-list size and the host Int range. There is no three-valued Undefined result; unsupported relation kinds are explicit registration failures.
Examples and tests
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.SatisfiedAmountFunction
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.inequality.Comparison
import fuookami.ospf.kotlin.math.symbol.inequality.LinearInequality
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 xPoly = LinearPolynomial(listOf(LinearMonomial(Flt64.one, x)), Flt64.zero)
val one = LinearPolynomial<Flt64>(emptyList(), Flt64.one)
val inequality = LinearInequality(xPoly, one, Comparison.LE, "x_le_1")
val satisfied = SatisfiedAmountFunction(
inequalities = listOf(inequality),
amount = UInt64.one,
epsilon = Flt64(1e-6),
bigM = Flt64(10.0),
converter = IntoValue.Identity,
name = "satisfied"
)
val value = satisfied.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero))
check(value == Flt64.one)use ospf_rust_core::symbol::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::{
InequalityFunction, SatisfiedAmountFunction,
};
let x = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let le = InequalityFunction::less_equal(1, "x_le_1", x, 1.0_f64, 10.0_f64);
let satisfied = SatisfiedAmountFunction::<f64>::new(
2,
"satisfied",
vec![le.result_variable().clone()],
)
.with_amount_range(Some(1), None);
let _count = satisfied.result_variable();- Core test:
SatisfiedAmountFunctionsGenericEvaluateTest.kt - Registration test:
FunctionSymbolSameAsGenericRegistrationTest.kt - Example directory (no dedicated satisfied-amount file): linear_function
Register le and satisfied with the model to enforce the indicator and count constraints. Rust source: satisfied_amount.rs and inequality.rs.