构建伊德里斯决策函数的证明

时间:2018-06-11 04:56:01

标签: proof idris theorem-proving

我正在尝试为表示模块化空间中两个连续元素的类型创建决策函数。

(此代码派生from this excellent answer到我之前的一个问题。)

data PreceedsN : Nat -> Nat -> Nat -> Type where
    PreceedsS : {auto prf : S a `LT` m} -> PreceedsN m a (S a)
    PreceedsZ : {auto prf : S a = m} -> PreceedsN m a Z

doesPreceedN : (m : Nat) -> (a : Nat) -> (b : Nat) -> Dec (PreceedsN m a b)
doesPreceedN m a b with (S a `cmp` m)
    doesPreceedN (S a + S d) a b     | CmpLT d with (S a `decEq` b)
        doesPreceedN (S a + S d) a b | CmpLT d | Yes prf = Yes ?bIsSa
        doesPreceedN (S a + S d) a b | CmpLT d | No contra = No ?bNotSa
    doesPreceedN (S m) m b     | CmpEQ   with (b `decEq` 0)
        doesPreceedN (S m) m Z | CmpEQ | Yes prf = Yes PreceedsZ
        doesPreceedN (S m) m b | CmpEQ | No contra = No ?saIsmAndbIsNotZ
    doesPreceedN (S m) (m + (S d)) b | CmpGT d = No ?saCannotBeGTm

我已经尽了最大努力,但我还不知道我自己构建必要的证据,特别是矛盾的证明。你能指导我如何填写上面代码中的一个或多个holes吗?

此外,如果您使用任何方便的工具,例如absurdimpossibletactics,您能解释一下它们的工作原理以及它们在证明中扮演的角色吗?

1 个答案:

答案 0 :(得分:2)

虽然prf - 构造函数中的PreceedsN需要LTE证据(LT a b只是LTE (S a) b的同义词),但您的第一个cmp 1}}只需拆分S a。相反,您应该直接获得LTE证明:

doesPreceedN m a b with (S (S a) `isLTE` m)

如果您不需要重复使用所有变量,省略with案例中的重复会让事情变得更漂亮。因此,要使用LTE重复您的版本,我们有:

  | (Yes lte) = case (decEq b (S a)) of
      Yes Refl => PreceedsS
      No notlte => No ?contra_notlte
  | (No notlte) with (decEq (S a) m)
    | Yes eq = case b of
      Z => Yes PreceedsZ
      (S b) => No ?contra_notZ
    | No noteq = No ?contra_noteq

在所有这些情况下,你需要一个来证明一些a -> Void,所以你可以假设,你有a。您可以创建一个引理(您的编辑器可能具有绑定)或使用带有案例拆分的lambda。对于像这里这样的短函数,我赞成后者。对于?contra_notZ

No (\contra => case contra of prec => ?contra_notZ)

如果你在prec上分开,你就有了:

No (\contra => case contra of PreceedsS => ?contra_notZ)

检查洞,你会发现你有:

notlte : LTE (S (S b)) m -> Void
prf : LTE (S (S b)) m

prfPreceedsS的隐含参数,因此要将其与范围匹配,您可以匹配它:

No (\contra => case contra of (PreceedsS {prf}) => notlte prf)

?contra_noteq可以解决问题。

?contra_notlte的lambda:

No notlte => No (\contra => case contra of
  PreceedsS => ?contra_notlte_1
  PreceedsZ => ?contra_notlte_2)

检查类型给出:

:t ?contra_notlte_1
notlte : (S a = S a) -> Void
:t ?contra_notlte_2
lte : LTE (S (S a)) m
prf : S a = m

?contra_notlte_2是最棘手的,因为您无法应用notlte。但是你可以看到lte : LTE (S m) m之后应该是假的,所以我们为它创建一个函数:

notLTE : Not (LTE (S n) n)
notLTE LTEZero impossible
notLTE (LTESucc lte) = notLTE lte

现在我们有:

PreceedsZ {prf} => notLTE ?contra_notlte_2
?contra_notlte_2 : LTE (S n) n

我尝试用(rewrite prf in lte)替换这个洞,但这不起作用,因为这个策略没有找到合适的属性来重写。但我们可以明确指出:

PreceedsZ {prf} => notLTE (replace prf lte)

> Type mismatch between
        LTE (S (S a)) m
and
        P (S a)

因此,我们通过设置P明确设置{P=(\x => LTE (S x) m)}

结果:

doesPreceedN : (m : Nat) -> (a : Nat) -> (b : Nat) -> Dec (PreceedsN m a b)
doesPreceedN m a b with (S (S a) `isLTE` m)
  | (Yes lte) = case (decEq b (S a)) of
    Yes Refl => Yes PreceedsS
    No notlte => No (\contra => case contra of
      PreceedsS => notlte Refl
      PreceedsZ {prf} => notLTE  (replace prf {P=(\x => LTE (S x) m)} lte))
  | (No notlte) with (decEq (S a) m)
    | Yes eq = case b of
      Z => Yes PreceedsZ
      (S b) => No (\contra => case contra of (PreceedsS {prf}) => notlte prf)
    | No noteq = No (\contra => case contra of 
        PreceedsS {prf} => notlte prf
        PreceedsZ {prf} => noteq prf)

replace : (x = y) -> P x -> P y只重写一个类型的一部分。 例如,对于(n + m = m + n),我们可以将n + m的{​​{1}}部分替换为Vect (n + m) a。这是Vect (m + n) a,因此P=\to_replace => Vect to_replace a只是一个函数P

Type -> Type中,我们需要明确doesPreceedN。大多数情况下,P(策略)可以自动找到此rewrite … in …并应用P。另一方面,replace只是一个简单的函数replace

:printdef replace