it-swarm.com.ru

Как сравнить две функции на эквивалентность, как в (λx.2 * x) == (λx.x + x)?

Есть ли способ сравнить две функции на равенство? Например, (λx.2*x) == (λx.x+x) должен возвращать true, потому что они, очевидно, эквивалентны.

71
MaiaVictor

Хорошо известно, что общее равенство функций в общем неразрешимо, поэтому вам придется выбрать подмножество интересующей вас проблемы. Вы можете рассмотреть некоторые из этих частичных решений:

  • арифметика Пресбургера является разрешимым фрагментом логики первого порядка + арифметика.
  • Пакет юниверс предлагает тесты на равенство функций для всех функций с конечной областью.
  • Вы можете проверить, что ваши функции равны на целой куче входных данных и рассматривать это как доказательство равенства на непроверенных входных данных; проверить QuickCheck .
  • Решатели SMT делают все возможное, иногда отвечая "не знаю" вместо "равно" или "не равно". В Hackage есть несколько привязок к SMT решателям; У меня недостаточно опыта, чтобы предложить лучший, но Томас М. Дюбюссон предлагает sbv .
  • Есть забавная линия исследований по определению равенства функций и других вещей о компактных функциях; Основы этого исследования описаны в блоге На первый взгляд невозможные функциональные программы . (Обратите внимание, что компактность является очень сильным и очень тонким условием! Это не то, что удовлетворяет большинство функций Хаскеля.)
  • Если вы знаете, что ваши функции линейны, вы можете найти основу для исходного пространства; тогда каждая функция имеет уникальное матричное представление.
  • Вы можете попытаться определить свой собственный язык выражений, доказать, что эквивалентность разрешима для этого языка, а затем встроить этот язык в 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

В дополнение к практическим примерам, приведенным в другом ответе, давайте выберем подмножество функций, выражаемых в типизированном лямбда-исчислении; мы также можем разрешить типы продуктов и сумм. Хотя проверить, равны ли две функции, можно так же просто, как применить их к переменной и сравнить результаты , мы не можем построить функцию равенства в самом языке программирования .

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

Доказательство того, что две функции равны, в общем неразрешимо, но все же можно доказать функциональное равенство в особых случаях, как в вашем вопросе.

Вот пример доказательства в 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