immut/context API

Luna-Flow/luna-poly/immut/context 提供 ContextPolynomial[A],即绑定到 VariableContext 的不可变多变量多项式。各项通过上下文按名字引用变量;该包还提供按名字求值、部分求值,以及用标量或多项式对变量进行同时代换(也支持 Luna-Flow/type_theory 名字)。

多项式以 TermPolynomial 或 SparsePolynomial 形式存储;由你在构造时选择,这一选择只影响开销,不影响结果。这些类型由 immut 门面包重新导出,示例即使用该门面包。代换的数学原理见 immut/context 设计。

大多数操作成对出现:普通形式在违反契约时中止(abort),而 *_checked 形式改为返回 None。各项都列出了相应的契约违反情形。

类型

ContextPolynomial

上下文 Γ\Gamma 中变量的多项式,连同 Γ\Gamma 本身。

type ContextPolynomial[A] derive(@debug.Debug)
pub impl[A : Eq + @luna-generic.AddMonoid] Add for ContextPolynomial[A]
pub impl[A : Eq + @luna-generic.AddMonoid + Neg] Sub for ContextPolynomial[A]
pub impl[A : Eq + @luna-generic.AddMonoid + Mul] Mul for ContextPolynomial[A]
pub impl[A : Eq + @luna-generic.Zero + Neg] Neg for ContextPolynomial[A]
pub impl[A : Show + @luna-generic.Zero] Show for ContextPolynomial[A]
pub impl[A] @core.ContextualPolynomial for ContextPolynomial[A]
pub impl[A] @core.HasArity for ContextPolynomial[A]
pub impl[A] @core.HasContext for ContextPolynomial[A]
pub impl[A] @core.HasShape for ContextPolynomial[A]
pub impl[A] @core.HasTermCount for ContextPolynomial[A]
pub impl[A] @core.IsZero for ContextPolynomial[A]

没有 Eq 实例;请改为比较 to_terms()(以及 context())。

ContextSubstitutionValue

代换中某一变量的替换值:一个系数,或同一上下文上的多项式。

pub(all) enum ContextSubstitutionValue[A] {
  Scalar(A)
  Polynomial(ContextPolynomial[A])
}

构造器是类型导向的,因此在代换列表中可以直接写 Scalar(2) 和 Polynomial(q),无需限定。

构造

ContextPolynomial::from_named_terms_as_terms, ContextPolynomial::from_named_terms_as_sparse

由命名项构造多项式,每一项是一组 (variable, exponent) 因子和一个系数,存储为项数组或稀疏映射。同一变量的重复因子会将指数相加,相等的单项式会合并。当某个因子使用了上下文之外的变量时中止(abort)。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::from_named_terms_as_terms(@Luna-Flow/luna-poly/core.VariableContext, Array[(Array[(@Luna-Flow/luna-poly/core.Variable, UInt)], A)]) -> Self[A]
pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::from_named_terms_as_sparse(@Luna-Flow/luna-poly/core.VariableContext, Array[(Array[(@Luna-Flow/luna-poly/core.Variable, UInt)], A)]) -> Self[A]

ContextPolynomial::from_named_terms_as_terms_checked, ContextPolynomial::from_named_terms_as_sparse_checked

与上面相同,但当某个因子使用了上下文中不包含的变量时返回 None。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::from_named_terms_as_terms_checked(@Luna-Flow/luna-poly/core.VariableContext, Array[(Array[(@Luna-Flow/luna-poly/core.Variable, UInt)], A)]) -> Self[A]?
pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::from_named_terms_as_sparse_checked(@Luna-Flow/luna-poly/core.VariableContext, Array[(Array[(@Luna-Flow/luna-poly/core.Variable, UInt)], A)]) -> Self[A]?

ContextPolynomial::from_term_polynomial, ContextPolynomial::from_sparse_polynomial

将按下标寻址的多项式绑定到上下文:多项式的变量 ii 成为上下文中下标为 ii 的变量。

pub fn[A] ContextPolynomial::from_term_polynomial(@Luna-Flow/luna-poly/core.VariableContext, @term.TermPolynomial[A]) -> Self[A]
pub fn[A] ContextPolynomial::from_sparse_polynomial(@Luna-Flow/luna-poly/core.VariableContext, @sparse.SparsePolynomial[A]) -> Self[A]

ContextPolynomial::constant

上下文上的常数多项式 cc,以稀疏形式存储。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::constant(@Luna-Flow/luna-poly/core.VariableContext, A) -> Self[A]

ContextPolynomial::variable

由上下文中的一个变量构成的多项式,以稀疏形式存储。当上下文不包含该变量时中止(abort)。

pub fn[A : Eq + @luna-generic.AddMonoid + @luna-generic.One] ContextPolynomial::variable(@Luna-Flow/luna-poly/core.VariableContext, @Luna-Flow/luna-poly/core.Variable) -> Self[A]

ContextPolynomial::variable_checked

与 variable 相同,但变量不在上下文中时返回 None。

pub fn[A : Eq + @luna-generic.AddMonoid + @luna-generic.One] ContextPolynomial::variable_checked(@Luna-Flow/luna-poly/core.VariableContext, @Luna-Flow/luna-poly/core.Variable) -> Self[A]?
test "construction" {
  let ctx = @immut.VariableContext::from_names(["x", "y"])
  let x = ctx.require_variable("x")
  let y = ctx.require_variable("y")
  let p = @immut.ContextPolynomial::from_named_terms_as_sparse(ctx, [
    ([(x, 2U)], 1),
    ([(x, 1U), (y, 1U)], 3),
    ([(y, 1U), (x, 1U)], 1),
    ([], 4),
  ])
  inspect(p, content="4 + 1 * x^2 + 4 * x * y")
  let q : @immut.ContextPolynomial[Int] = @immut.ContextPolynomial::variable(ctx, y)
  inspect(q, content="1 * y")
  let stranger = @immut.VariableContext::from_names(["z"]).require_variable("z")
  let missing : @immut.ContextPolynomial[Int]? = @immut.ContextPolynomial::variable_checked(ctx, stranger)
  assert_true(missing is None)
}

查询与转换

ContextPolynomial::context

返回多项式所绑定的上下文。

pub fn[A] ContextPolynomial::context(Self[A]) -> @Luna-Flow/luna-poly/core.VariableContext

ContextPolynomial::to_terms

按存储顺序返回按下标寻址的项:项存储为降序,稀疏存储为升序。

pub fn[A] ContextPolynomial::to_terms(Self[A]) -> Array[(@Luna-Flow/luna-poly/core.ExponentVector, A)]

ContextPolynomial::to_term_polynomial, ContextPolynomial::to_sparse_polynomial

以所请求的按下标寻址存储形式返回该多项式,必要时进行转换。上下文会被丢弃。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::to_term_polynomial(Self[A]) -> @term.TermPolynomial[A]
pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::to_sparse_polynomial(Self[A]) -> @sparse.SparsePolynomial[A]

ContextPolynomial::term_count, ContextPolynomial::arity, ContextPolynomial::is_zero, ContextPolynomial::shape

项数、使用中的变量个数(所用的最大下标加一)、是否没有任何项,以及 PolynomialShape::Contextual(context~, arity~, term_count~)。

pub fn[A] ContextPolynomial::term_count(Self[A]) -> Int
pub fn[A] ContextPolynomial::arity(Self[A]) -> Int
pub fn[A] ContextPolynomial::is_zero(Self[A]) -> Bool
pub fn[A] ContextPolynomial::shape(Self[A]) -> @Luna-Flow/luna-poly/core.PolynomialShape

ContextPolynomial::to_string

使用上下文中的变量名,按存储顺序把各项渲染为 c * x^2 * y 的形式;零多项式打印为系数零。

pub fn[A : Show + @luna-generic.Zero] ContextPolynomial::to_string(Self[A]) -> String

求值

ContextPolynomial::eval

按下标给出取值进行求值,与 TermPolynomial::eval 相同:values[i] 是上下文中变量 ii 的取值。当 values.length() < arity() 时中止(abort)。

pub fn[A : @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval(Self[A], Array[A]) -> A

ContextPolynomial::eval_checked

与 eval 相同,但当 values 短于 arity() 时返回 None。

pub fn[A : @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_checked(Self[A], Array[A]) -> A?

ContextPolynomial::eval_named

按变量给出取值进行求值。上下文中下标小于 arity() 的每个变量都必须恰好赋值一次;对上下文中更靠后的变量的赋值会被接受并忽略。遇到上下文之外的变量、缺失的赋值或重复的赋值时中止(abort)。

pub fn[A : @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_named(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, A)]) -> A

ContextPolynomial::eval_named_checked

与 eval_named 相同,但在同样的情形下返回 None。

pub fn[A : @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_named_checked(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, A)]) -> A?
test "evaluation" {
  let ctx = @immut.VariableContext::from_names(["x", "y"])
  let x = ctx.require_variable("x")
  let y = ctx.require_variable("y")
  let p = @immut.ContextPolynomial::from_named_terms_as_terms(ctx, [([(x, 2U)], 1), ([(x, 1U), (y, 1U)], 3)])
  inspect(p.eval([2, 5]), content="34")
  inspect(p.eval_named([(y, 5), (x, 2)]), content="34")
  assert_true(p.eval_named_checked([(x, 2)]) is None)
  assert_true(p.eval_named_checked([(x, 2), (x, 3), (y, 5)]) is None)
}

部分求值与代换

下面的所有形式都返回同一上下文上的多项式;被赋值的变量只是不再出现在其中。替换是同时进行的,一遍完成:替换多项式本身不会再被代换。

ContextPolynomial::substitute

把列出的每个变量替换为标量或基于相等上下文的多项式,未列出的变量保持不变。当变量不在上下文中、某变量被列出两次,或替换多项式的上下文不同时中止(abort)。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::substitute(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, ContextSubstitutionValue[A])]) -> Self[A]

ContextPolynomial::substitute_checked

与 substitute 相同,但在同样的情形下返回 None。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::substitute_checked(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, ContextSubstitutionValue[A])]) -> Self[A]?

ContextPolynomial::substitute_names

与 substitute 相同,但变量以 Luna-Flow/type_theory 的 Name 值指定,并在多项式的上下文中解析。当名字不在上下文中时也会中止(abort);两个同名条目视为重复。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::substitute_names(Self[A], Array[(@Luna-Flow/type_theory/core.Name, ContextSubstitutionValue[A])]) -> Self[A]

ContextPolynomial::substitute_names_checked

与 substitute_names 相同,但在同样的情形下返回 None。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::substitute_names_checked(Self[A], Array[(@Luna-Flow/type_theory/core.Name, ContextSubstitutionValue[A])]) -> Self[A]?

ContextPolynomial::eval_partial, ContextPolynomial::eval_partial_checked

对部分变量代入标量:eval_partial(a) 就是把每个值包装为 Scalar 后的 substitute,失败情形相同。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_partial(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, A)]) -> Self[A]
pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_partial_checked(Self[A], Array[(@Luna-Flow/luna-poly/core.Variable, A)]) -> Self[A]?

ContextPolynomial::eval_partial_named, ContextPolynomial::eval_partial_named_checked

使用 type_theory 名字的同类操作:以标量为值的 substitute_names。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_partial_named(Self[A], Array[(@Luna-Flow/type_theory/core.Name, A)]) -> Self[A]
pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::eval_partial_named_checked(Self[A], Array[(@Luna-Flow/type_theory/core.Name, A)]) -> Self[A]?

无论输入采用何种存储,每次代换的结果都以稀疏形式存储。

test "substitution" {
  let ctx = @immut.VariableContext::from_names(["x", "y"])
  let x = ctx.require_variable("x")
  let y = ctx.require_variable("y")
  let p = @immut.ContextPolynomial::from_named_terms_as_sparse(ctx, [([(x, 2U)], 1), ([(y, 1U)], 1)])
  let y_plus_1 = @immut.ContextPolynomial::from_named_terms_as_sparse(ctx, [([(y, 1U)], 1), ([], 1)])
  inspect(p.substitute([(x, Polynomial(y_plus_1))]), content="1 + 3 * y + 1 * y^2")
  inspect(p.eval_partial([(x, 3)]), content="9 + 1 * y")
  let named = p.eval_partial_named([(y.to_type_theory_name(), 10)])
  inspect(named, content="10 + 1 * x^2")
  assert_true(p.substitute_checked([(x, Scalar(1)), (x, Scalar(2))]) is None)
  assert_true(p.substitute_names_checked([(@tt_core.Name::new("w"), Scalar(1))]) is None)
}

算术

ContextPolynomial::add, ContextPolynomial::sub, ContextPolynomial::mul, ContextPolynomial::neg

运算符 +、-、* 和一元 -。当两个上下文不同时,二元运算符会中止(abort)。两个项存储的操作数得到项存储的结果,两个稀疏操作数得到稀疏结果,混合操作数得到稀疏结果。取负保持存储形式不变。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::add(Self[A], Self[A]) -> Self[A]
pub fn[A : Eq + @luna-generic.AddMonoid + Neg] ContextPolynomial::sub(Self[A], Self[A]) -> Self[A]
pub fn[A : Eq + @luna-generic.AddMonoid + Mul] ContextPolynomial::mul(Self[A], Self[A]) -> Self[A]
pub fn[A : Eq + @luna-generic.Zero + Neg] ContextPolynomial::neg(Self[A]) -> Self[A]

ContextPolynomial::add_checked, ContextPolynomial::mul_checked

与 + 和 * 相同,但上下文不同时返回 None。

pub fn[A : Eq + @luna-generic.AddMonoid] ContextPolynomial::add_checked(Self[A], Self[A]) -> Self[A]?
pub fn[A : Eq + @luna-generic.AddMonoid + Mul] ContextPolynomial::mul_checked(Self[A], Self[A]) -> Self[A]?

ContextPolynomial::pow

以相同存储形式返回 pep^e;pow(0) 是常数一。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::pow(Self[A], UInt) -> Self[A]
test "arithmetic" {
  let ctx = @immut.VariableContext::from_names(["x"])
  let x = ctx.require_variable("x")
  let a = @immut.ContextPolynomial::from_named_terms_as_terms(ctx, [([(x, 1U)], 1)])
  let b = @immut.ContextPolynomial::constant(ctx, 1)
  inspect((a + b).pow(2), content="1 + 2 * x + 1 * x^2")
  let other = @immut.ContextPolynomial::constant(@immut.VariableContext::from_names(["t"]), 1)
  assert_true(a.add_checked(other) is None)
  assert_true(a.mul_checked(b) is Some(_))
}

泛型访问

ContextPolynomial::ops

返回 ContextOps 记录:from_terms 构造项存储的多项式,eval_named_checked、add_checked 和 mul_checked 即上面的方法。

pub fn[A : Eq + @luna-generic.AddMonoid + Mul + @luna-generic.One] ContextPolynomial::ops() -> @Luna-Flow/luna-poly/core.ContextOps[Self[A], A]
test "ops" {
  let ctx = @immut.VariableContext::from_names(["x"])
  let x = ctx.require_variable("x")
  let ops = @immut.ContextPolynomial::ops()
  let p = ops.from_terms(ctx, [(@immut.ExponentVector::from_array([1U]), 2)])
  debug_inspect(ops.eval_named_checked(p, [(x, 5)]), content="Some(10)")
}

已弃用

为源码兼容而保留的隐藏方法形式:

已弃用替代方案
p.output(logger)to_string 或字符串插值
p.to_repr()Repr(p) 或 debug_inspect