Skip to content

Sine ​

SinFunction is a sampled, piecewise-linear approximation of sine. It is a linear modeling primitive, not an exact trigonometric solver function.

WARNING

The default model is defined only by five samples on [−π,π]. Values outside the breakpoint domain evaluate to null, and values between samples follow straight-line interpolation.

Contract ​

  • Input: x: LinearPolynomial<V>.
  • Output: result, a linear polynomial supplied by the delegated univariate piecewise function.
  • Samples: List<Point<Dim2, Flt64>> containing (x,sin⁡x) points; the default list is generated by the implementation.
  • Generic values use V : RealNumber<V>, V : NumberField<V> and an IntoValue<V> converter.
  • Registration adds the ordinary univariate piecewise-linear helper variables and constraints.

Definition and mathematical model ​

For ordered sample points (ai,bi), each segment is

si=bi+1−biai+1−ai,ci=bi−siai,y=six+ci(ai≤x≤ai+1).

The default samples are exactly:

(−π,0),(−π2,−1),(0,0),(π2,1),(π,0).

Thus the default is a five-point linear interpolation of sine, with no periodic extension and no exact sin⁡(x) evaluation.

Solver mathematical model ​

Kotlin delegates to the binary-selector model. For every sampled segment [ti,ti+1] with affine interpolation fi(x)=aix+bi, it registers

∑izi=1,ti−MiL(1−zi)≤x≤ti+1+MiU(1−zi),fi(x)−Mi−(1−zi)≤y≤fi(x)+Mi+(1−zi),zi∈{0,1}.

Rust fixes 32 segments. With segment width h, selector zi∈{0,1}, and gated offset 0≤δi≤hzi, its equivalent registered form is

∑izi=1,x=∑i(tizi+δi),y=∑i(sin⁡tizi+aiδi).

Neither solver receives an exact trigonometric constraint.

Current API ​

Kotlin ​

Source: Sin.kt (SinFunction)

The factory also accepts an explicit samplingPoints list. Points must be finite, have at least two entries, and have strictly increasing x-coordinates when the delegated piecewise implementation is registered or evaluated.

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.SinFunction
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
import fuookami.ospf.kotlin.core.variable.RealVar

val x = RealVar("x")
val xPoly = LinearPolynomial(
    listOf(LinearMonomial(Flt64.one, x)), Flt64.zero
)
val sine = SinFunction(
    x = xPoly,
    converter = IntoValue.Identity,
    name = "sine"
)
val value = sine.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero))
check(value != null && value == Flt64.zero)

Rust ​

Rust exposes SinFunction:

rust
SinFunction::new(
    id: u64,
    name: &str,
    input: Linear<V>,
) -> SinFunction<V>

The constructor fixes the modeling grid to 32 segments over [-π,π], creates a bounded continuous result_variable(), and registers piecewise-linear segment constraints. Rust's token evaluator computes sin(input) directly and has no Kotlin-style configurable samplingPoints argument; this means direct evaluation can differ from the piecewise model, especially outside the modeling grid.

Evaluate versus solver ​

evaluate follows the same delegated piecewise interpolation used by registration, but returns null for a missing input or an x-value outside the first and last breakpoints. Solver registration does not add an exact trigonometric relation; it registers the piecewise linear approximation and its Big-M/segment constraints.

Boundaries, tolerance, and Undefined ​

This function has no three-valued condition classifier and no tolerance parameter of its own. The numeric conversion and piecewise validation are the relevant boundaries. A malformed sample list (fewer than two points, non-finite values, duplicate or descending x-coordinates) fails the delegated piecewise validation. The endpoint convention is closed for the available segments.

Examples and tests ​

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.SinFunction
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
import fuookami.ospf.kotlin.core.variable.RealVar

val x = RealVar("x")
val xPoly = LinearPolynomial(
    listOf(LinearMonomial(Flt64.one, x)), Flt64.zero
)
val sine = SinFunction(
    x = xPoly,
    converter = IntoValue.Identity,
    name = "sine"
)
val value = sine.evaluate(mapOf<Symbol, Flt64>(x to Flt64.zero))
check(value != null && value == Flt64.zero)
rust
use ospf_rust_core::symbol::flatten::{Linear, LinearMonomial};
use ospf_rust_core::symbol::function::SinFunction;

let input = Linear::new(vec![LinearMonomial::new(1.0, 0)], 0.0);
let sine = SinFunction::new(1, "sin", input);
let _result = sine.result_variable();

Rust source: sin.rs.