Skip to content

Quadratic Minimum ​

QuadraticMinFunction computes the minimum of a list of quadratic polynomials and offers an exact selector mode or a lower-envelope relaxation.

Contract ​

  • Input: polynomials: List<QuadraticPolynomial<V>>.
  • Output/helper: real resultVar named by appending _min to name.
  • Direct evaluation returns the minimum; missing symbols or an empty candidate list result in null.
  • exact = true creates one binary selector per candidate and aims to enforce exact minimum; exact = false registers only y≤pi constraints.
  • Generic values require V : RealNumber<V>, V : Ring<V>, V : NumberField<V> and an IntoValue<V> converter.

WARNING

exact = false is a lower-envelope relaxation. Without an objective or another constraint pushing y upward, it need not equal the mathematical minimum.

Definition and mathematical model ​

For candidates pi and result y, both modes add

y≤pi(i=0,…,n−1).

Exact mode additionally creates ui∈{0,1}:

y≥pi−Mi(1−ui),∑iui=1.

The direct evaluator always computes minipi, independent of exact.

Solver mathematical model ​

Kotlin ​

Let the quadratic candidates be pi and let the result be a signed continuous variable y∈R. Every mode submits:

y−pi≤0,∀i.

With exact = false, these are the only upper-bound rows; an objective or another constraint must push y to the actual minimum. With exact = true, the function also creates ui∈{0,1} and submits:

y−pi−Miui≥−Mi,∀i,∑iui=1.

Equivalently, y≥pi−Mi(1−ui). An explicit Mi takes precedence; otherwise candidate bounds are used before falling back to each candidate's default Big-M.

Rust ​

Rust first creates a signed continuous bridge bi for every quadratic candidate and submits bi=pi(x). It then applies the same Min model to the bridges:

y≤bi,

and, in exact mode:

y≥bi−Mi(1−ui),ui∈{0,1},∑iui=1.

The minimum rows therefore agree across the languages; the main structural difference is Rust's explicit bridge for every quadratic candidate.

Current API ​

Kotlin ​

Source: QuadraticMin.kt (QuadraticMinFunction)

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.QuadraticMinFunction
import fuookami.ospf.kotlin.core.token.AutoTokenTable
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Quadratic
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.monomial.QuadraticMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.QuadraticPolynomial
import fuookami.ospf.kotlin.core.variable.RealVar

val x = RealVar("x")
val y = RealVar("y")
val first = QuadraticPolynomial(
    listOf(QuadraticMonomial.quadratic(Flt64.one, x, y)), Flt64.one
)
val second = QuadraticPolynomial(
    listOf(QuadraticMonomial.linear(Flt64.one, x)), Flt64.two
)
val minimum = QuadraticMinFunction(
    polynomials = listOf(first, second),
    exact = true,
    bigM = Flt64(10.0),
    converter = IntoValue.Identity,
    name = "quadratic_min"
)
val tokens = AutoTokenTable<Flt64>(Quadratic, false)
tokens.add(listOf(x, y))
val value = minimum.prepare(
    mapOf<Symbol, Flt64>(x to Flt64.two, y to Flt64(5.0)),
    tokens,
    IntoValue.Identity
)
check(value == Flt64(4.0))
tokens.close()

Rust ​

Rust exposes QuadraticMinFunction<V>:

rust
QuadraticMinFunction::new(
    id: u64,
    name: &str,
    inputs: Vec<Quadratic<V>>,
    exact: bool,
) -> QuadraticMinFunction<V>

result_variable returns the inner name + "_min" variable and with_declared_dependencies preserves explicit dependency IDs. Each input is bridged by QuadraticLinearFunction; exact = true creates the inner binary selectors, while exact = false keeps only the lower-envelope upper inequalities. calculate_value always computes the mathematical minimum. When token bounds are available, mechanism_constraints_with_tokens infers Big-M from the original quadratic candidates; otherwise the generic fallback policy is used. Rust has no bigM constructor argument on this type.

rust
use ospf_rust_core::symbol::flatten::{Quadratic, QuadraticMonomial};
use ospf_rust_core::symbol::function::QuadraticMinFunction;

let first = Quadratic::new(
    vec![QuadraticMonomial::new_quadratic(1.0, 0, 1)],
    1.0,
);
let second = Quadratic::new(
    vec![QuadraticMonomial::new_linear(1.0, 0)],
    2.0,
);
let minimum = QuadraticMinFunction::new(15, "quadratic_min", vec![first, second], true);
assert!(minimum.result_variable().name().contains("quadratic_min_min"));

Evaluate versus solver ​

Direct evaluation is always the exact minimum. Exact solver mode needs valid Big-M ranges for all candidates; relaxed mode only guarantees an upper bound on y for every candidate. The result bridge is signed, so negative candidate minima are representable.

Boundaries, tolerance, and Undefined ​

The input list should be non-empty; an empty list makes the direct minimum null and provides no meaningful solver model. Missing values return null. There is no tolerance or three-valued Undefined state. Invalid or insufficient Big-M values can make exact registration fail or weaken it.

Examples and tests ​

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.QuadraticMinFunction
import fuookami.ospf.kotlin.core.token.AutoTokenTable
import fuookami.ospf.kotlin.math.algebra.number.Flt64
import fuookami.ospf.kotlin.math.symbol.Quadratic
import fuookami.ospf.kotlin.math.symbol.Symbol
import fuookami.ospf.kotlin.math.symbol.monomial.QuadraticMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.QuadraticPolynomial
import fuookami.ospf.kotlin.core.variable.RealVar

val x = RealVar("x")
val y = RealVar("y")
val first = QuadraticPolynomial(
    listOf(QuadraticMonomial.quadratic(Flt64.one, x, y)), Flt64.one
)
val second = QuadraticPolynomial(
    listOf(QuadraticMonomial.linear(Flt64.one, x)), Flt64.two
)
val minimum = QuadraticMinFunction(
    polynomials = listOf(first, second),
    exact = true,
    bigM = Flt64(10.0),
    converter = IntoValue.Identity,
    name = "quadratic_min"
)
val tokens = AutoTokenTable<Flt64>(Quadratic, false)
tokens.add(listOf(x, y))
val value = minimum.prepare(
    mapOf<Symbol, Flt64>(x to Flt64.two, y to Flt64(5.0)),
    tokens,
    IntoValue.Identity
)
check(value == Flt64(4.0))
tokens.close()
rust
use ospf_rust_core::symbol::FunctionSymbol;
use ospf_rust_core::symbol::flatten::{Quadratic, QuadraticMonomial};
use ospf_rust_core::symbol::function::QuadraticMinFunction;
use ospf_rust_core::token::{MutableTokenList, Token, VecTokenList};
use ospf_rust_core::variable::{ContinuousVariableItem, VariableId};

let x = ContinuousVariableItem::create(VariableId::standalone(0), "x");
let y = ContinuousVariableItem::create(VariableId::standalone(1), "y");
let mut tokens = VecTokenList::<f64>::new();
let tx = Token::from_generic(x, 0);
tx.set_result(2.0);
tokens.add_token(tx);
let ty = Token::from_generic(y, 1);
ty.set_result(5.0);
tokens.add_token(ty);
let minimum = QuadraticMinFunction::new(
    16,
    "quadratic_min",
    vec![
        Quadratic::new(vec![QuadraticMonomial::new_quadratic(1.0, 0, 1)], 1.0),
        Quadratic::new(vec![QuadraticMonomial::new_linear(1.0, 0)], 2.0),
    ],
    true,
);
assert_eq!(minimum.calculate_value(&tokens, false), Some(4.0));