Maximum
MaxFunction represents the maximum of one or more linear polynomials:
Contract
- Input: a non-empty
List<LinearPolynomial<V>>(n >= 1). - Output:
resultVar, aRealVar, exposed asresultPolynomial. evaluateevaluates every input and returns their maximum; a missing symbol value makes it returnnull.Vmust implementRealNumber<V>andNumberField<V>; pass the matchingIntoValue<V>converter.
Mathematical definition
The exact selector model uses binary selectorVars
With the selector for one candidate equal to one, that candidate is forced to equal the result; the remaining inequalities force the result to be at least every candidate.
Domain and boundaries
The current result variable is a signed RealVar. When every candidate has finite bounds, the result variable's range is tightened to bigM, each candidate's finite bounds are used when available (each candidate gets the widest candidate upper bound minus its own lower bound); otherwise the fallback Big-M is currently
Current API
Kotlin
Source: Max.kt (MaxFunction)
MaxFunction(
polynomials: List<LinearPolynomial<V>>,
bigM: V? = null,
converter: IntoValue<V>,
name: String = "max",
displayName: String? = null
)The companion factory also provides fromSymbols for a list of LinearIntermediateSymbol<V>. The ordinary constructor is the clearest choice when the candidates are already LinearPolynomial<V> values.
Rust
Rust exposes MaxFunction over flattened Linear<V> expressions:
MaxFunction::new(
id: u64,
name: &str,
polynomials: Vec<Linear<V>>,
exact: bool,
) -> MaxFunction<V>exact = true creates one binary selector per candidate and registers an exactly-one selector model. With exact = false, Rust registers only the lower bounds result >= p_i; an objective or another upper bound is then needed to make the result equal the maximum. result_variable(), polynomials(), and exact() expose the state. Rust's result is a continuous variable; callers must provide suitable bounds when the model requires them. Rust's MinMaxFunction and MaxMinFunction are the corresponding wrapper symbols without the exact flag.
Solver mathematical model
With result exact = true pass
Rust exact = false registers only
evaluate versus solver
evaluate is a direct fold over all candidate values and has no Big-M or variable-domain side effects. Solver registration adds the selector model and, when every candidate has finite bounds, tightens the result domain to the candidate bounds; an undersized Big-M can make the solver model infeasible despite a valid direct evaluation.
Examples and tests
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.MaxFunction
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.inequality.eq
import fuookami.ospf.kotlin.math.symbol.monomial.LinearMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.LinearPolynomial
val x = RealVar("x")
val y = RealVar("y")
val xPoly = LinearPolynomial(listOf(LinearMonomial(Flt64.one, x)), Flt64.zero)
val yPoly = LinearPolynomial(listOf(LinearMonomial(Flt64.one, y)), Flt64.zero)
val max = MaxFunction(
polynomials = listOf(xPoly, yPoly),
bigM = Flt64(10.0),
converter = IntoValue.Identity,
name = "max"
)
val value = max.evaluate(mapOf<Symbol, Flt64>(x to Flt64.two, y to Flt64(5.0)))
check(value != null && (value eq Flt64(5.0)))use ospf_rust_core::symbol::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::MaxFunction;
let first = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let second = Linear::new(vec![LinearMonomial::new(1.0, 1)], 0.0);
let max = MaxFunction::new(1, "max", vec![first, second], true);
assert!(max.exact());
let _result = max.result_variable();Complete example: MaxTest.kt
Core validation: MaxAndMaskingFunctionGenericEvaluateTest.kt
Rust source and parity coverage: max.rs and gurobi_linear_function_kotlin_parity.rs.
MinMaxFunction and MaxMinFunction
Despite their names, MinMaxFunction computes the maximum by delegating every evaluation, helper-variable, and constraint operation to an inner MaxFunction. MaxMinFunction computes the minimum by delegating to an inner MinFunction. The names describe the optimization interpretation, not a different aggregation algorithm. Both wrappers accept the same polynomials, optional bigM, converter, name, and optional displayName parameters. Their fromSymbols factories accept List<LinearIntermediateSymbol<V>> and return a LinearFunctionSymbolAdapter; the adapter is only a bridge to the intermediate-symbol API.
Source: MinMax.kt (MinMaxFunction and MaxMinFunction)
val minMax = MinMaxFunction(
polynomials = listOf(xPoly, yPoly),
bigM = Flt64(10.0),
converter = IntoValue.Identity,
name = "min_max"
)
val maxMin = MaxMinFunction(
polynomials = listOf(xPoly, yPoly),
bigM = Flt64(10.0),
converter = IntoValue.Identity,
name = "max_min"
)