1

我想证明在有限数量的情况下参数化的属性。我想将问题划分为每个案例一个实例,并分别解决每个实例。这是一个清理事情的例子:

module Minimal
open FStar.List
open FStar.Tactics
open FStar.Reflection.Data

unfold let lst = [0;1]

unfold let prop i = 
  match i with
  | 0 -> i == 0
  | 1 -> i == 1
  | _ -> False

val propHolds (i:int) : Lemma (requires (List.mem i lst)) (ensures (prop i))

在这种情况下,案例由列表 lst 定义。我可以很容易地证明 propHolds:

let propHolds i =
  assert_by_tactic (prop 0) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized"; trivial ());
  assert_by_tactic (prop 1) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized"; trivial ())

但我显然不想为每种情况写一个单独的 assert_by_tactic (不是当可能有数千个......)。我想以某种方式为 lst 中的所有元素自动生成上述证明。

我尝试了各种各样的东西,这里是其中之一:

  assert_by_tactic (let rec props i =
                       if i = 0 then prop 0
                       else (prop i) /\ (props (i-1))
                    in
                      props 1) (fun () -> norm[delta;primops;iota;zeta]; dump "normalized")

不幸的是,这并没有完全达到我想要的效果,assert_by_tactic 失败了(并且没有以我期望的方式减少)。我想我遗漏了一些关于标准化的明显内容,但是在 F* 中执行此操作的规范方法是什么?如果解决方案指向失败的“案例”/断言(如果存在),则加分。

4

1 回答 1

0

F* 的类型系统仅确保项的弱归一化。类型良好的开放式术语可能会出现分歧,例如,当在不一致的上下文中缩减时。为了防止这种情况,F* 规范化器采用了各种启发式方法,并且默认情况下,保守地拒绝减少未减少匹配体中的递归调用。这就是阻止 List.mem 完全减少为级联未减少的 if/then/else 的原因(if/then/else 只是布尔匹配的糖)。

List.memP,在这种情况下,来自 F* 标准库的相关函数对归约更友好,因为它不会在内部阻止未归约的匹配。注意,List.memP 不必总是比 List.mem 更易于归约——后者是布尔值,因此在某些情况下它可以计算更多(例如,List.mem 3 [1;2;3]将归约到 就好true);

试试这个程序:

module Minimal
open FStar.Tactics

let lst = [0;1;2;3;4;5;6;7;8;9;10]

let prop i =
  match i with
  | 0 -> i == 0
  | 1 -> i == 1
  | 2 -> i == 2
  | 3 -> i == 3
  | 4 -> i == 4
  | 5 -> i == 5
  | 6 -> i == 6
  | 7 -> i == 7
  | 8 -> i == 8
  | 9 -> i == 9
  | 10 -> i == 10
  | _ -> False

let propHolds (i:int) =
  assert (List.memP i lst ==> prop i) 
      by (dump "A";
          norm [delta_only [`%List.memP; `%lst]; iota; zeta; simplify];
          dump "B")

dump B处,您会看到假设简化为嵌套析取。Z3可以从那里轻松证明目标。

这是另一种方法,这次没有策略。

let trigger_norm (a:Type) 
  : Lemma
    (requires a)
    (ensures (Pervasives.norm [delta_only [`%List.memP; `%lst]; iota; zeta; simplify] a))
  = ()


let propHolds (i:int) 
  : Lemma
    (requires List.memP i lst)
    (ensures prop i)
  = trigger_norm (List.memP i lst)

现在,作为对 jebus 下面的评论的回应,您可以更进一步并使用一种策略来证明后置条件,尽管 SMT 求解器在这方面确实非常快……所以我不会为此使用策略,除非您有一些特定的这样做的有力理由。

这是另一种解决方案:

module SO
open FStar.Tactics

let lst = [0;1;2;3;4;5;6;7;8;9;10]

let pred i =
  match i with
  | 0 -> i == 0
  | 1 -> i == 1
  | 2 -> i == 2
  | 3 -> i == 3
  | 4 -> i == 4
  | 5 -> i == 5
  | 6 -> i == 6
  | 7 -> i == 7
  | 8 -> i == 8
  | 9 -> i == 9
  | 10 -> i == 10
  | _ -> False

let case_impl (a b c:Type) 
  : Lemma
    (requires (a ==> c) /\ (b ==> c))
    (ensures (a \/ b) ==> c) 
  = ()

let solve_pred_impl () : Tac unit =
    let eq = implies_intro () in
    rewrite eq;
    norm [delta_only [`%pred]; iota];
    trivial()

let test i =  
  assert (List.memP i lst ==> pred i)
      by (norm [delta_only [`%List.memP; `%lst]; iota; zeta; simplify];
          let _ = repeat 
            (fun () ->
              mapply (`case_impl); 
              split();
              solve_pred_impl()) in
          solve_pred_impl())
于 2019-02-11T22:10:57.157 回答