Slack
Current API
Kotlin
For two linear polynomials x and y, let d = x - y. SlackFunction<V> exposes non-negative violation helpers:
withNegative = true, withPositive = false: max(0, -d)
withNegative = false, withPositive = true: max(0, d)
withNegative = true, withPositive = true: |d|The implementation is Slack.kt. Its primary constructor is:
SlackFunction(
x: LinearPolynomial<V>,
y: LinearPolynomial<V>,
type: VariableTypeKind = UContinuous,
withNegative: Boolean = true,
withPositive: Boolean = true,
threshold: Boolean = false,
constraint: Boolean = true,
converter: IntoValue<V>,
name: String,
displayName: String? = null
)Overloads accept LinearIntermediateSymbol<V> and ToLinearPolynomial<V> (Slack.kt:270-336). At least one of withNegative and withPositive must be true (Slack.kt:55-57). Integer type creates UIntVar helpers; a continuous type creates URealVar helpers (Slack.kt:102-104). When both directions are enabled, Kotlin also creates one binary branch helper (<name>_side), so the helper list is neg, pos, and side.
Rust
Rust exposes SlackFunction as an absolute difference:
SlackFunction::new(
id: u64,
name: &str,
left: Linear<V>,
right: Linear<V>,
) -> SlackFunction<V>
SlackFunction::with_target(
id: u64,
name: &str,
left: Linear<V>,
right_value: V,
) -> SlackFunction<V>
SlackFunction::with_big_m(
id: u64,
name: &str,
left: Linear<V>,
right: Linear<V>,
big_m: V,
) -> SlackFunction<V>The Rust result is always |left - right| and exposes result_variable(). It has no Kotlin withNegative/withPositive, threshold, constraint, or variable-type parameters; use with_target for a constant right side and with_big_m when the unbounded default (
Derived symbol and evaluation
The helper expression is
neg and pos are exposed as nullable linear polynomials, and resultPolynomial is the sum of whichever helpers were requested (Slack.kt:72-94). Direct evaluation uses only x and y: both helpers give |x-y|, only neg gives max(0,y-x), and only pos gives max(0,x-y) (Slack.kt:106-119). It returns null when either input is unresolved.
Solver mathematical model
With constraint = true:
when both
withNegativeandwithPositiveare enabled, Kotlin registers the exact four-row absolute-value model below, regardless ofthreshold:where
, , and is the binary side helper. These rows force without an objective. with only one helper enabled,
threshold = falseregisters the equalityx + neg - pos = y;threshold = trueregisters the corresponding one-sided inequality (x + neg >= yforneg,x - pos <= yforpos).
With constraint = false, no relation between the inputs and helpers is registered (Slack.kt:130-132). evaluate always computes the mathematical violation from the inputs, independently of registered helper values. The model-side value is therefore exact only when the relevant helper expression is minimized or otherwise constrained to its minimum; without minimization, the relation permits inflated slack.
Rust creates signed result
For valid with_big_m accepts an explicit bound.
References
- Implementation:
Slack.kt - Complete example:
SlackTest.kt - Core tests:
FunctionSymbolRegressionTest.kt,FunctionSymbolPiecewiseGenericRegistrationTest.kt
Examples and tests
import kotlinx.coroutines.runBlocking
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.monomial.LinearMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.LinearPolynomial
import fuookami.ospf.kotlin.core.model.mechanism.LinearMechanismModel
import fuookami.ospf.kotlin.core.model.mechanism.LinearMetaModel
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.LinearFunctionSymbolAdapter
import fuookami.ospf.kotlin.core.symbol.function.SlackFunction
import fuookami.ospf.kotlin.core.variable.RealVar
import fuookami.ospf.kotlin.utils.functional.Ok
val x = RealVar("x")
val xPoly = LinearPolynomial(
listOf(LinearMonomial(Flt64.one, x)), Flt64.zero
)
val zeroPoly = LinearPolynomial<Flt64>(emptyList(), Flt64.zero)
val slack = SlackFunction(
x = xPoly,
y = zeroPoly,
converter = IntoValue.Identity,
name = "slack"
)
val symbol = LinearFunctionSymbolAdapter(slack, IntoValue.Identity)
val model = LinearMetaModel<Flt64>(name = "slack-model", converter = IntoValue.Identity)
check(model.add(x) is Ok)
check(model.add(symbol) is Ok)
check(model.minimize(symbol) is Ok)
val mechanism = runBlocking {
LinearMechanismModel.invoke<Flt64>(metaModel = model, concurrent = false)
}
check(mechanism is Ok)
model.close()use ospf_rust_core::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::SlackFunction;
let left = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let slack = SlackFunction::with_target(1, "slack", left, 0.0_f64);
let _result = slack.result_variable();SlackFunction is a MathFunctionSymbol, not itself a LinearIntermediateSymbol. Wrap it with LinearFunctionSymbolAdapter before adding its result to a model objective, as in FunctionSymbolRegressionTest.kt and MinimizeMaximizeSymbolTest.kt:
Rust source: slack.rs.