web-dev-qa-db-ja.com

(λx.2* x)==(λx.x+ x)のように、2つの関数の等価性を比較する方法は?

2つの関数が等しいかどうかを比較する方法はありますか?例えば、 (λx.2*x) == (λx.x+x)はtrueを返す必要があります。これらは明らかに同等であるためです。

71
MaiaVictor

一般的な関数の等価性は一般的に決定できないことはよく知られているので、関心のある問題のサブセットを選択する必要があります。これらの部分的な解決策のいくつかを検討することができます。

  • Presburger算術 は、1次論理+算術の決定可能なフラグメントです。
  • niverse パッケージは、有限領域を持つ全関数の関数等価性テストを提供します。
  • 関数が入力の束全体で等しいことを確認し、テストされていない入力の同等性の証拠として扱うことができます。チェックアウト QuickCheck
  • SMTソルバーは最善の努力を払い、「等しい」または「等しくない」の代わりに「わからない」と応答することがあります。 HackageのSMTソルバーにはいくつかのバインディングがあります。最高のものを提案するのに十分な経験はありませんが、Thomas M. DuBuissonは sbv を提案しています。
  • 関数の平等を決定することや、コンパクト関数に関するその他のことについての楽しい研究があります。この研究の基本はブログ投稿で説明されています 一見不可能な機能プログラム 。 (コンパクト性は非常に強力で微妙な条件であることに注意してください!ほとんどのHaskell関数が満たすものではありません。)
  • 関数が線形であることがわかっている場合は、ソース空間の基礎を見つけることができます。次に、すべての関数に一意のマトリックス表現があります。
  • 独自の表現言語を定義し、この言語の等価性を決定できることを証明してから、その言語をHaskellに埋め込むことができます。これは最も柔軟ですが、進歩を遂げる最も難しい方法でもあります。
124
Daniel Wagner

これは一般的には決定できませんが、適切なサブセットについては、SMTソルバーを使用して効果的に今日実行できます。

$ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> :m Data.SBV
Prelude Data.SBV> (\x ->  2 * x) === (\x -> x + x :: SInteger)
Q.E.D.
Prelude Data.SBV> (\x ->  2 * x) === (\x -> 1 + x + x :: SInteger)
Falsifiable. Counter-example:
  s0 = 0 :: Integer

詳細については、以下を参照してください。 https://hackage.haskell.org/package/sbv

42
alias

他の答えで与えられた実用的な例に加えて、型付きラムダ計算で表現可能な関数のサブセットを選んでみましょう。製品と合計タイプも許可できます。 2つの関数が等しいかどうかのチェックは 変数に適用して結果を比較する と同じくらい簡単ですが、等値関数 プログラミング言語自体内 を構築することはできません。

ETA: λProlog は(型付きラムダ計算)関数を操作するための論理プログラミング言語です。

11
lukstafi

2年が経過しましたが、この質問に少しコメントを加えたいと思います。もともと、(λx.2*x)(λx.x+x)と等しいかどうかを判断する方法があるかどうかを尋ねました。 λ計算の加算と乗算は、次のように定義できます。

add = (a b c -> (a b (a b c)))
mul = (a b c -> (a (b c)))

ここで、次の用語を正規化すると:

add_x_x = (λx . (add x x))
mul_x_2 = (mul (λf x . (f (f x)))

あなたが得る:

result = (a b c -> (a b (a b c)))

両方のプログラム用。通常の形式は等しいため、両方のプログラムは明らかに等しくなります。これは一般的には機能しませんが、実際には多くの用語で機能します。たとえば、(λx.(mul 2 (mul 3 x))(λx.(mul 6 x))はどちらも同じ標準形式です。

9
MaiaVictor

Mathematicaのようなシンボリック計算のある言語では:

enter image description here

または、C#と コンピュータ代数ライブラリ

MathObject f(MathObject x) => x + x;
MathObject g(MathObject x) => 2 * x;

{
    var x = new Symbol("x");

    Console.WriteLine(f(x) == g(x));
}

上記は、コンソールに「True」と表示されます。

2
dharmatech

2つの関数が等しいことを証明することは一般的に決定できませんが、質問のように特別な場合でも機能的な同等性を証明できます。

これがLeanのサンプル証明です

def foo : (λ x, 2 * x) = (λ x, x + x) :=
begin
  apply funext, intro x,
  cases x,
  { refl },
  { simp,
    dsimp [has_mul.mul, nat.mul],
    have zz : ∀ a : nat, 0 + a = a := by simp,
    rw zz }
end

Coq、Agda、Idrisなどの他の依存型付き言語でも同じことができます。

上記は戦術的なスタイルの証明です。生成されるfoo(証明)の実際の定義は、手書きで書くのは非常に口がききます:

def foo : (λ (x : ℕ), 2 * x) = λ (x : ℕ), x + x :=
funext
  (λ (x : ℕ),
     nat.cases_on x (eq.refl (2 * 0))
       (λ (a : ℕ),
          eq.mpr
            (id_locked
               ((λ (a a_1 : ℕ) (e_1 : a = a_1) (a_2 a_3 : ℕ) (e_2 : a_2 = a_3), congr (congr_arg eq e_1) e_2)
                  (2 * nat.succ a)
                  (nat.succ a * 2)
                  (mul_comm 2 (nat.succ a))
                  (nat.succ a + nat.succ a)
                  (nat.succ a + nat.succ a)
                  (eq.refl (nat.succ a + nat.succ a))))
            (id_locked
               (eq.mpr
                  (id_locked
                     (eq.rec (eq.refl (0 + nat.succ a + nat.succ a = nat.succ a + nat.succ a))
                        (eq.mpr
                           (id_locked
                              (eq.trans
                                 (forall_congr_eq
                                    (λ (a : ℕ),
                                       eq.trans
                                         ((λ (a a_1 : ℕ) (e_1 : a = a_1) (a_2 a_3 : ℕ) (e_2 : a_2 = a_3),
                                             congr (congr_arg eq e_1) e_2)
                                            (0 + a)
                                            a
                                            (zero_add a)
                                            a
                                            a
                                            (eq.refl a))
                                         (propext (eq_self_iff_true a))))
                                 (propext (implies_true_iff ℕ))))
                           trivial
                           (nat.succ a))))
                  (eq.refl (nat.succ a + nat.succ a))))))
0
Slavomir Kaslev