mutable の設計

設計目標

mutable ファサードは、luna-poly の実行指向の半分を 1 回のインポートで利用できるようにし、その背後の層は、immut 層が計算するものとまったく同じ結果を計算しながら、アルゴリズムが多項式をその場で更新できるようにします。この層では性能が最優先ですが、副作用は呼び出し側から見える場所、つまり変更することを名前で示すメソッドの中にとどめなければなりません。

数学的背景

ミュータブルな多項式は、多項式を値とする 変数 です。値はイミュータブル層と同じ数学的対象であり、同じ正規形をとります。インプレース演算は代入 p←p∘qp \leftarrow p \circ q であり、この層の契約は次のとおりです。

p.op_inplace(q)  leaves p equal to p_before op q,\texttt{p.op\_inplace(q)}\ \text{ leaves } p \text{ equal to } \texttt{p\_before op q},

ここで p_before op q はイミュータブル層のアルゴリズムで計算されます。すべての観測(次数、項、評価、等価性)は現在の値だけに依存します。

設計上の判断

immut と同じ名前を持つファサード

このファサードは、core、luna-generic の代数トレイト、4 つのミュータブル表現を、immut ファサードと同じ名前で再エクスポートします。アルゴリズムを層の間で切り替えるのは、ほとんどの場合インポートの変更だけで済みます。能力トレイトや演算レコードに対して書かれたジェネリックコードは両方で動作します。

変更はオプトインで、名前で示す

レシーバを変更するのは set_coefficient、clear、add_term_inplace と *_inplace メソッドだけです。演算子とその他すべてのメソッドは新しい値を返します。ミュータブルな型でも x + y が x を変更することはないので、算術式はどちらの層でも同じように読めます。

正規形はすべての return の前に復元する

変更を伴う各メソッドは、コンテナを正規形のまま残します。係数配列は切り詰められ、項配列はソートとマージが済み、疎なマップはゼロを含みません。不変条件は immut と同じなので、==、degree、size、各種の形状は同じ意味を持ちます。

アルゴリズムは委譲し、更新は特化する

自明でないアルゴリズム(Karatsuba、合成、微分、累乗、多変数の積、コンテキストのロジック全体)は immut に一度だけ実装し、変換を介して利用します。ミュータブルなパッケージが直接実装するのは、インプレースの格納の恩恵を受けるものだけです。係数のセッター、密な表現のインプレース加算、疎な表現の単一項の更新がそれにあたります。両層が一致することは consistency テスト で確認しています。

境界での所有権

格納された値自体がイミュータブルである場合(コンテキストのセル)を除き、すべての変換は格納をコピーまたは再構築します。そのため、ミュータブルなコンテナがイミュータブルな値や他のコンテナと格納を共有することはありません。

  • from_immut と to_immut はコピーする(dense、term、sparse)か、イミュータブルな値を共有する(context)。
  • copy() は独立したコンテナを返す。
  • to_coefficients や to_terms などの問い合わせメソッドは新しい配列を返す。

immut との API の対称性

両層は名前、引数の順序、チェック付き版の規約で一致しています。文書化された相違点は次のとおりです。

項目immutmutable
変換なしすべての型に from_immut、to_immut
コピーとリセット値にはどちらも不要copy, clear (Copyable, Clearable, MutablePolynomial)
dense の更新from_coefficients で再構築set_coefficient(チェック付き版なし)、add_inplace、mul_inplace、scale_inplace
term の更新+, scaleadd_term_inplace, add_inplace, mul_inplace, scale_inplace
sparse の更新add_term(新しい値を返す)set_coefficient, add_term_inplace, add_inplace, mul_inplace, scale_inplace
context の二項演算add_checked, mul_checkedadd_inplace、mul_inplace(中断する)。チェック付き版は ops() 経由
context の束縛イミュータブルな term/sparse 多項式を受け取るミュータブルな term/sparse 多項式を受け取る
代入のペイロードimmut ContextSubstitutionValueミュータブルな多項式を保持する独自の ContextSubstitutionValue
sparse の copy の制約—Eq + AddMonoid の係数が必要
ExponentVector, Variable, VariableContextcore の型同じ core の型

正しさ / 不変条件

  • すべてのコンテナで、すべての公開呼び出しの後に正規形であること。
  • すべての演算について、ミュータブルな結果を to_immut で変換したものは、変換した入力に対するイミュータブルな結果に等しいこと。
  • ミュータブルなコンテナが、他のいかなる値ともミュータブルな格納を共有しないこと。
  • コンテナを自分自身の引数として渡す(p.add_inplace(p)、p.mul_inplace(p))と、2p2p と p2p^2 になること。

採用しなかった代替案

  • イミュータブルな対応物を持たないミュータブル専用の型。 値のセマンティクスのほうが安全な既定であり、変更は呼び出し箇所ごとに選ぶ最適化です。
  • 変更を伴う演算子。 すべての a * b が副作用を持ちうるものになってしまいます。
  • ミュータブル層のための 独立したアルゴリズム実装 は、一致させなければならないコードを倍にしてしまいます。

境界

  • このファサードは独自の関数や型を追加しません。
  • コンテナはスナップショットではありません。コンテナを別の束縛に代入すると共有されます。copy() を使ってください。
  • 委譲される演算は変換のコストを払います。この層が最適化するのは更新であり、アルゴリズムそのものではありません。
  • イミュータブル層が行わないこと(除算、因数分解、グレブナー基底)は、この層も行いません。