Skip to content

逻辑与 ​

契约 ​

AndFunction<V> 接受一个或多个线性多项式并暴露二值结果。当且仅当每个输入多项式都非零时结果为 1;至少一个输入为零时结果为 0。当前 API 对 V : RealNumber<V> & NumberField<V> 泛型化,并且是线性函数符号。

这不是只面向布尔变量的运算。输入可以是连续变量,也可以包含中间符号。

定义与真值表 ​

对于输入多项式 p1,…,pn,令 ai 表示非零指示量:

ai={1,pi≠00,pi=0,y={1,∑i=1nai=n0,otherwise

两个输入时的真值表如下:

p1 nonzerop2 nonzeroy
nono0
noyes0
yesno0
yesyes1

构造器要求至少有一个输入多项式。

边界、tolerance 与 Undefined ​

evaluate() 将每个求值结果与精确的零比较。缺少某个多项式输入时返回 null,不会返回独立的 Undefined 值。

求解器注册使用共享的非零指示构造。给定 tolerance t 时,指示量 0 表示零带 |pi|≤t;给定严格边界 g 时,指示量 1 表示 pi≥g 或 pi≤−g。位于间隔 t<|pi|<g 内的值没有可用分支,可能使模型不可行。

当前源码中的常量是 NONZERO_TOLERANCE = 1e-10 和 STRICT_BOUNDARY = NONZERO_TOLERANCE * 16 + 16 * 2^-52,不是 1e-6 或 0.5。省略 bigM 时,先根据每个多项式的有限范围推导;没有可用范围时,范围工具回退到 BIG_M_DEFAULT = 1e6。

当前 API ​

Kotlin ​

主构造器为:

kotlin
AndFunction(
    polynomials: List<LinearPolynomial<V>>,
    converter: IntoValue<V>,
    bigM: V? = null,
    tolerance: V? = null,
    strictBoundary: V? = null,
    name: String = "and",
    displayName: String? = null
)

伴生 invoke 接受 polynomials、converter、bigM、name 和 displayName。另外的 fromLinearPolynomials 工厂接受 List<ToLinearPolynomial<V>>,返回 LinearFunctionSymbolAdapter<V>。

Rust ​

源码:and.rs

Rust 接收平展后的输入,并提供 AndFunction::new(id, name, polynomials)、AndFunction::named(name, polynomials) 和 AndFunction::auto(polynomials)。可通过 result_variable()、indicator_variables() 和 side_variables() 获取公开变量。Rust 没有公开的 tolerance 参数;求值器使用 epsilon 级别的非零判定,机理 Big-M 则从 token 范围推导,或回退到核心默认值。

rust
AndFunction::new(id: u64, name: &str, polynomials: Vec<Linear<V>>) -> Self
AndFunction::named(name: impl AsRef<str>, polynomials: Vec<Linear<V>>) -> Self
AndFunction::auto(polynomials: Vec<Linear<V>>) -> Self

求解器数学模型 ​

函数名称为 name 时,当前实现创建:

  • name_and:二值结果;
  • name_and_nz{i}:每个输入对应一个非零指示量;
  • name_and_side{i}:每个非零指示量对应一个符号侧辅助量。

helperVariables 包含结果、全部非零指示量和全部侧辅助量;当每个输入多项式都恰好是系数为 1 的二值变量时,只注册结果,并跳过下方的指示块。对每个 pi,令非零标志为 ai、方向标志为 si、零值容差为 t、严格边界为 g。共享的四约束 Big-M 模型表示

ai=0⇒−t≤pi≤t,(ai,si)=(1,1)⇒pi≥g,(ai,si)=(1,0)⇒pi≤−g.

实现把这些蕴含展开成四条线性不等式,再添加 AND 约束:

∑iai≥ny,y≤ai(1≤i≤n).

在全部输入均为二值变量时,指示块被替换为直接约束:对每个二值输入 zi 有 y≤zi,另有 ∑izi≥ny+1−n。

公开的 resultPolynomial 是 name_and 的单位系数多项式。实现位于 And.kt,并向 AbstractLinearMechanismModel 注册。

Rust 同样先注册非零指标块,再注册相同的 AND 约束;其数值阈值和 Big-M 由 Rust 机理层选择,而不是由 Kotlin 构造参数指定。

evaluate() 与求解器模型的差异 ​

直接求值使用精确的 v == 0/v != 0 语义。求解器模型有意把零带与严格非零分支分开,因此数值上非零但位于 tolerance 与 strict boundary 之间的值会被 evaluate() 接受,却没有求解器分支。应根据模型的数值格点一致设置 tolerance 和 strictBoundary。

当前最小示例 ​

kotlin
import fuookami.ospf.kotlin.core.solver.value.IntoValue
import fuookami.ospf.kotlin.core.symbol.function.AndFunction
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.monomial.LinearMonomial
import fuookami.ospf.kotlin.math.symbol.polynomial.LinearPolynomial

fun main() {
    val x = RealVar("x")
    val y = RealVar("y")
    val xPoly = LinearPolynomial(
        monomials = listOf(LinearMonomial(Flt64.one, x)),
        constant = Flt64.zero
    )
    val yPoly = LinearPolynomial(
        monomials = listOf(LinearMonomial(Flt64.one, y)),
        constant = Flt64.zero
    )
    val function = AndFunction(
        polynomials = listOf(xPoly, yPoly),
        converter = IntoValue.Identity,
        name = "and"
    )

    check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64.one, y to Flt64(2.0))) == Flt64.one)
    check(function.evaluate(mapOf<Symbol, Flt64>(x to Flt64.one, y to Flt64.zero)) == Flt64.zero)
}
rust
use ospf_rust_core::symbol::flatten::Linear;
use ospf_rust_core::symbol::function::AndFunction;
use ospf_rust_core::symbol::FunctionSymbol;
use ospf_rust_core::token::VecTokenList;

let function = AndFunction::named(
    "and",
    vec![Linear::new(vec![], 1.0), Linear::new(vec![], 2.0)],
);
let value = <AndFunction as FunctionSymbol>::calculate_value(
    &function,
    &VecTokenList::<f64>::new(),
    false,
);
assert_eq!(value, Some(1.0));

源码与 core 测试 ​

相关页面 ​