core チュートリアル
このチュートリアルでは、luna-poly の共有レイヤー、すなわち指数ベクトル、名前付き変数、形状、能力トレイト、演算レコードの使い方を示します。最後には、ライブラリのすべての多項式表現で動く関数を 1 つ書き、そこに独自の表現を組み込めるようになります。
クイックスタート
モジュールを追加します。
moon add Luna-Flow/luna-poly@0.2.0
Luna-Flow/type_theory/core と衝突しないようにエイリアスを付けてパッケージをインポートし、具体的な多項式のためにイミュータブルなファサードもインポートします。
import {
"Luna-Flow/luna-poly/core" @poly_core,
"Luna-Flow/luna-poly/immut",
}
最小限の有用なプログラムは、2 つの単項式を構築して掛け合わせます。
test "quick start" {
let xy = @poly_core.ExponentVector::from_array([1U, 1])
let x2 = @poly_core.ExponentVector::from_array([2U])
let product = xy * x2
inspect(product, content="x^3x_1")
inspect(product.degree(), content="4")
}
[1, 1] は 、[2] は です。その積 は x^3x_1 と表示されます。このチュートリアルのすべての型は immut および mutable ファサードからも再エクスポートされているので、@immut.ExponentVector は @poly_core.ExponentVector と同じ型です。
日常的なタスク
指数ベクトルを読み取り、更新する
指数ベクトルは末尾の零を決して格納せず、末尾を越えて読むと 0 が得られます。
test "exponent vector basics" {
let v = @poly_core.ExponentVector::from_array([2U, 0, 1, 0, 0])
debug_inspect(v.to_array(), content="[2, 0, 1]")
inspect(v.length(), content="3")
inspect(v[2], content="1")
inspect(v[10], content="0")
let w = v.with_exponent(2, 0)
debug_inspect(w.to_array(), content="[2]")
debug_inspect(v.to_array(), content="[2, 0, 1]")
assert_true(@poly_core.ExponentVector::from_array([1U, 0]) == @poly_core.ExponentVector::from_array([1U]))
}
with_exponent は新しいベクトルを返し、v は変わりません。
単項式をソートする
単項式はまず全次数で、次に異なる変数のうち添字が最大のもので順序付けられます。
test "monomial order" {
let monomials = [
@poly_core.ExponentVector::from_array([1U, 0, 1]),
@poly_core.ExponentVector::from_array([0U, 2]),
@poly_core.ExponentVector::one(),
@poly_core.ExponentVector::from_array([3U]),
@poly_core.ExponentVector::from_array([1U]),
]
monomials.sort()
inspect(
monomials.map(m => m.to_string()).join(" < "),
content="1 < x < x_1^2 < xx_2 < x^3",
)
}
と はどちらも次数 です。最後に異なるのは変数 で、そこでは の指数の方が大きいので です。この順序が単項式順序である理由は core の設計 で説明しています。
変数に名前を付ける
VariableContext は各名前に安定した添字を与えます。
test "contexts" {
let ctx = @poly_core.VariableContext::from_names(["x", "y"])
let (ctx, t) = ctx.extend_with("t")
inspect(ctx, content="VariableContext(x, y, t)")
inspect(t.index(), content="2")
match ctx.variable("y") {
Some(y) => inspect(y.index(), content="1")
None => fail("y should exist")
}
assert_true(ctx.variable("z") is None)
assert_true(ctx.extend_checked("x") is None)
}
コンテキストは値です。extend_with は新しいコンテキストと追加した変数を返し、既存の変数の番号を付け替えることはありません。
type_theory と連携する
名前が Luna-Flow/type_theory から来る場合は、コンテキストを通じて双方向に変換します。
test "type theory names" {
let ctx = @poly_core.VariableContext::from_names(["x", "y"])
let y = ctx.require_variable("y")
let name : @tt_core.Name = y.to_type_theory_name()
inspect(name.text(), content="y")
assert_true(ctx.variable_by_type_theory_name(name) == Some(y))
assert_true(ctx.variable_by_type_theory_name(@tt_core.Name::new("q")) is None)
}
これにはインポートに "Luna-Flow/type_theory/core" @tt_core が必要です。名前は添字を忘れ、コンテキストがそれを復元します。
すべての表現に対して 1 つの関数を書く
型パラメータを能力バンドルで制約し、トレイトメソッドを修飾形式で呼び出します。
fn[P : @poly_core.MultivariatePolynomial] summary(p : P) -> String {
if @poly_core.IsZero::is_zero(p) {
return "zero"
}
"\{@poly_core.HasTermCount::term_count(p)} terms in \{@poly_core.HasArity::arity(p)} variables"
}
test "generic over representations" {
let terms = [([2U, 1], 3), ([0U, 0, 1], 1), ([], -2)]
let a = @immut.TermPolynomial::from_array(terms)
let b = @immut.SparsePolynomial::from_array(terms)
let c = @mutable.SparsePolynomial::from_array(terms)
inspect(summary(a), content="3 terms in 3 variables")
inspect(summary(b), content="3 terms in 3 variables")
inspect(summary(c), content="3 terms in 3 variables")
inspect(summary(@immut.TermPolynomial::from_array([([1U], 0)])), content="zero")
}
アルゴリズムが構築や算術も必要とする場合は、その型の演算レコードを渡します。
fn[P, A] square_all(ops : @poly_core.UnivariateOps[P, A], ps : Array[P]) -> Array[A] {
ps.map(p => ops.eval(ops.pow(p, 2), ops.coefficient(p, 0)))
}
test "operation records" {
let p = @immut.DensePolynomial::from_coefficients([1, 1])
let q = @mutable.DensePolynomial::from_coefficients([2, 0, 1])
debug_inspect(square_all(@immut.DensePolynomial::ops(), [p]), content="[4]")
debug_inspect(square_all(@mutable.DensePolynomial::ops(), [q]), content="[36]")
}
square_all は、 を定数項として を任意の一変数表現に対して計算します。、 です。
組み合わせる前にチェックする
形状によって、2 つの値が同じ多項式環に属するかどうかがわかります。
test "shapes" {
let ctx = @poly_core.VariableContext::from_names(["x"])
let other = @poly_core.VariableContext::from_names(["u"])
let x = ctx.require_variable("x")
let u = other.require_variable("u")
let p = @immut.ContextPolynomial::from_named_terms_as_sparse(ctx, [([(x, 1U)], 1)])
let q = @immut.ContextPolynomial::from_named_terms_as_sparse(other, [([(u, 1U)], 1)])
let sp = @poly_core.HasShape::shape(p)
let sq = @poly_core.HasShape::shape(q)
assert_false(sp.is_compatible_with(sq))
assert_true(p.add_checked(q) is None)
inspect(sp.arity(), content="1")
}
さらに進む
独自の表現を組み込む
能力トレイトはオープンなので、独自の型も汎用コードに参加できます。観測とバンドルを実装します。
struct Monic {
degree : Int
}
impl @poly_core.HasShape for Monic with fn shape(self) {
@poly_core.PolynomialShape::univariate(self.degree + 1)
}
impl @poly_core.HasLength for Monic with fn length(self) {
self.degree + 1
}
impl @poly_core.HasDegree for Monic with fn degree(self) {
Some(self.degree)
}
impl @poly_core.IsZero for Monic with fn is_zero(_self) {
false
}
impl @poly_core.UnivariatePolynomial for Monic
fn[P : @poly_core.UnivariatePolynomial] describe_degree(p : P) -> String {
match @poly_core.HasDegree::degree(p) {
Some(d) => "degree \{d}"
None => "zero polynomial"
}
}
test "own representation" {
inspect(describe_degree(Monic::{ degree: 3 }), content="degree 3")
inspect(describe_degree(@immut.DensePolynomial::from_coefficients([0, 0])), content="zero polynomial")
}
演算については、UnivariateOps::new、MultivariateOps::new、ContextOps::new でレコードを構築し、core API に記載された順序で関数を渡します。
中断せずに失敗を扱う
中断 (abort) するすべてのアクセサには、代わりに None を返す *_checked の対があります。Option のコンビネータや match でつなげます。
fn exponent_of(v : @poly_core.ExponentVector, i : Int) -> String {
match v.get_checked(i) {
Some(e) => e.to_string()
None => "invalid index"
}
}
test "checked" {
let v = @poly_core.ExponentVector::from_array([4U])
inspect(exponent_of(v, 0), content="4")
inspect(exponent_of(v, -1), content="invalid index")
}
Option は演算が なぜ 失敗したかを伝えません。メッセージが必要な場合は自分で前提条件を確認してください。
よくある落とし穴
- インポートのエイリアス。
Luna-Flow/luna-poly/coreとLuna-Flow/type_theory/coreはどちらもデフォルトで@coreになります。クイックスタートのように、少なくとも一方にエイリアスを付けてください。 - 指数は
UIntです。 リテラルは1Uと書き (少なくとも配列の最初の要素)、指数の和は を法としてラップアラウンドすることに注意してください。 - 表示される単項式には区切りがありません。
x * x_1はxx_1と表示されます。曖昧さのない形式が必要な場合はto_array()を使ってください。 - 複数の制約を持つ型パラメータ。
fn[P : A + B]の内部や、メソッドがスーパートレイトに由来する場合は、p.method()ではなくTrait::method(p)と書いてください。 - コンテキストは構造的です。 同じ名前を同じ順序で構築した 2 つのコンテキストは等しく、それらの多項式は組み合わせられます。
- 形状はサイズを無視します。 2 つの多変数の形状はアリティにかかわらず互換です。互換性が排除するのは、異なる族やコンテキストを混ぜることだけです。
次のステップ
- core API はすべての項目をシグネチャ付きで列挙しています。
- core の設計 は単項式順序と名前の往復を導出しています。
- 具体的な多項式については
immutチュートリアル に、名前付き変数と代入については context チュートリアル に進んでください。 Luna-Flow/type_theoryはName型を説明しています。