src/HOL/Tools/function_package/lexicographic_order.ML
author bulwahn
Wed Feb 07 13:05:28 2007 +0100 (2007-02-07)
changeset 22258 0967b03844b5
parent 21817 0210a5db2013
child 22309 87ec1ca65312
permissions -rw-r--r--
changes in lexicographic_order termination tactic
bulwahn@21131
     1
(*  Title:       HOL/Tools/function_package/lexicographic_order.ML
krauss@21201
     2
    ID:          $Id$
bulwahn@21131
     3
    Author:      Lukas Bulwahn, TU Muenchen
bulwahn@21131
     4
bulwahn@21131
     5
Method for termination proofs with lexicographic orderings.
bulwahn@21131
     6
*)
bulwahn@21131
     7
bulwahn@21131
     8
signature LEXICOGRAPHIC_ORDER =
bulwahn@21131
     9
sig
krauss@21319
    10
  val lexicographic_order : Proof.context -> Method.method
krauss@21510
    11
krauss@21510
    12
  (* exported for use by size-change termination prototype.
krauss@21510
    13
     FIXME: provide a common interface later *)
krauss@21510
    14
  val mk_base_funs : typ -> term list
krauss@21510
    15
krauss@21237
    16
  val setup: theory -> theory
bulwahn@21131
    17
end
bulwahn@21131
    18
bulwahn@21131
    19
structure LexicographicOrder : LEXICOGRAPHIC_ORDER =
bulwahn@21131
    20
struct
bulwahn@21131
    21
bulwahn@21131
    22
(* Theory dependencies *)
bulwahn@21131
    23
val measures = "List.measures"
bulwahn@21131
    24
val wf_measures = thm "wf_measures"
bulwahn@21131
    25
val measures_less = thm "measures_less"
bulwahn@21131
    26
val measures_lesseq = thm "measures_lesseq"
krauss@21237
    27
                      
bulwahn@21131
    28
fun del_index (n, []) = []
bulwahn@21131
    29
  | del_index (n, x :: xs) =
krauss@21237
    30
    if n>0 then x :: del_index (n - 1, xs) else xs 
bulwahn@21131
    31
bulwahn@21131
    32
fun transpose ([]::_) = []
bulwahn@21131
    33
  | transpose xss = map hd xss :: transpose (map tl xss)
bulwahn@21131
    34
bulwahn@21131
    35
fun mk_sum_case (f1, f2) =
bulwahn@21131
    36
    case (fastype_of f1, fastype_of f2) of
krauss@21237
    37
      (Type("fun", [A, B]), Type("fun", [C, D])) =>
krauss@21237
    38
      if (B = D) then
krauss@21237
    39
        Const("Datatype.sum.sum_case", (A --> B) --> (C --> D) --> Type("+", [A,C]) --> B) $ f1 $ f2
krauss@21237
    40
      else raise TERM ("mk_sum_case: range type mismatch", [f1, f2]) 
krauss@21237
    41
    | _ => raise TERM ("mk_sum_case", [f1, f2])
krauss@21237
    42
                 
bulwahn@21131
    43
fun dest_wf (Const ("Wellfounded_Recursion.wf", _) $ t) = t
bulwahn@21131
    44
  | dest_wf t = raise TERM ("dest_wf", [t])
krauss@21237
    45
                      
bulwahn@21131
    46
datatype cell = Less of thm | LessEq of thm | None of thm | False of thm;
krauss@21237
    47
         
bulwahn@21131
    48
fun is_Less cell = case cell of (Less _) => true | _ => false  
krauss@21237
    49
                                                        
bulwahn@21131
    50
fun is_LessEq cell = case cell of (LessEq _) => true | _ => false
krauss@21237
    51
                                                            
bulwahn@21131
    52
fun thm_of_cell cell =
bulwahn@21131
    53
    case cell of 
krauss@21237
    54
      Less thm => thm
krauss@21237
    55
    | LessEq thm => thm
krauss@21237
    56
    | False thm => thm
krauss@21237
    57
    | None thm => thm
krauss@21237
    58
                  
bulwahn@21131
    59
fun mk_base_fun_bodys (t : term) (tt : typ) =
bulwahn@21131
    60
    case tt of
krauss@21237
    61
      Type("*", [ft, st]) => (mk_base_fun_bodys (Const("fst", tt --> ft) $ t) ft) @ (mk_base_fun_bodys (Const("snd", tt --> st) $ t) st)      
krauss@21237
    62
    | _ => [(t, tt)]
krauss@21237
    63
           
bulwahn@21131
    64
fun mk_base_fun_header fulltyp (t, typ) =
krauss@21816
    65
    Abs ("x", fulltyp, Const("Nat.size", typ --> HOLogic.natT) $ t)
krauss@21237
    66
         
bulwahn@21131
    67
fun mk_base_funs (tt: typ) = 
bulwahn@21131
    68
    mk_base_fun_bodys (Bound 0) tt |>
krauss@21237
    69
                      map (mk_base_fun_header tt)
bulwahn@22258
    70
bulwahn@22258
    71
fun mk_funorder_funs (tt : typ) =
bulwahn@22258
    72
    case tt of
bulwahn@22258
    73
      Type("+", [ft, st]) => product (mk_funorder_funs ft) (mk_funorder_funs st)
bulwahn@22258
    74
                                    |> map mk_sum_case
bulwahn@22258
    75
    | _ => [Abs ("x", tt, HOLogic.zero), Abs ("x", tt, HOLogic.Suc_zero)]
bulwahn@22258
    76
bulwahn@21131
    77
fun mk_ext_base_funs (tt : typ) =
bulwahn@21131
    78
    case tt of
krauss@21237
    79
      Type("+", [ft, st]) =>
krauss@21237
    80
      product (mk_ext_base_funs ft) (mk_ext_base_funs st)
krauss@21237
    81
              |> map mk_sum_case
krauss@21237
    82
    | _ => mk_base_funs tt
bulwahn@22258
    83
bulwahn@22258
    84
fun mk_all_measure_funs (tt : typ) =
bulwahn@22258
    85
    case tt of
bulwahn@22258
    86
      Type("+", _) => (mk_ext_base_funs tt) @ (mk_funorder_funs tt)
bulwahn@22258
    87
    | _ => mk_base_funs tt
krauss@21237
    88
           
bulwahn@21131
    89
fun dest_term (t : term) =
bulwahn@21131
    90
    let
krauss@21237
    91
      val (vars, prop) = (FundefLib.dest_all_all t)
krauss@21237
    92
      val prems = Logic.strip_imp_prems prop
krauss@21237
    93
      val (tuple, rel) = Logic.strip_imp_concl prop
krauss@21237
    94
                         |> HOLogic.dest_Trueprop 
krauss@21237
    95
                         |> HOLogic.dest_mem
krauss@21237
    96
      val (lhs, rhs) = HOLogic.dest_prod tuple
bulwahn@21131
    97
    in
krauss@21237
    98
      (vars, prems, lhs, rhs, rel)
bulwahn@21131
    99
    end
krauss@21237
   100
    
bulwahn@21131
   101
fun mk_goal (vars, prems, lhs, rhs) rel =
bulwahn@21131
   102
    let 
krauss@21237
   103
      val concl = HOLogic.mk_binrel rel (lhs, rhs) |> HOLogic.mk_Trueprop
krauss@21237
   104
    in  
krauss@21237
   105
      Logic.list_implies (prems, concl) |>
krauss@21237
   106
                         fold_rev FundefLib.mk_forall vars
bulwahn@21131
   107
    end
krauss@21237
   108
    
bulwahn@21131
   109
fun prove (thy: theory) (t: term) =
bulwahn@21131
   110
    cterm_of thy t |> Goal.init 
bulwahn@21131
   111
    |> SINGLE (CLASIMPSET auto_tac) |> the
krauss@21237
   112
    
bulwahn@21131
   113
fun mk_cell (thy : theory) (vars, prems) (lhs, rhs) = 
krauss@21237
   114
    let 
krauss@21237
   115
      val goals = mk_goal (vars, prems, lhs, rhs) 
krauss@21237
   116
      val less_thm = goals "Orderings.less" |> prove thy
bulwahn@21131
   117
    in
krauss@21237
   118
      if Thm.no_prems less_thm then
krauss@21237
   119
        Less (Goal.finish less_thm)
krauss@21237
   120
      else
krauss@21237
   121
        let
krauss@21237
   122
          val lesseq_thm = goals "Orderings.less_eq" |> prove thy
krauss@21237
   123
        in
krauss@21237
   124
          if Thm.no_prems lesseq_thm then
krauss@21237
   125
            LessEq (Goal.finish lesseq_thm)
krauss@21237
   126
          else 
krauss@21237
   127
            if prems_of lesseq_thm = [HOLogic.Trueprop $ HOLogic.false_const] then False lesseq_thm
krauss@21237
   128
            else None lesseq_thm
krauss@21237
   129
        end
bulwahn@21131
   130
    end
krauss@21237
   131
    
bulwahn@22258
   132
fun mk_row (thy: theory) measure_funs (t : term) =
bulwahn@21131
   133
    let
krauss@21237
   134
      val (vars, prems, lhs, rhs, _) = dest_term t
bulwahn@22258
   135
      val lhs_list = map (fn x => x $ lhs) measure_funs
bulwahn@22258
   136
      val rhs_list = map (fn x => x $ rhs) measure_funs
bulwahn@21131
   137
    in
krauss@21237
   138
      map (mk_cell thy (vars, prems)) (lhs_list ~~ rhs_list)
bulwahn@21131
   139
    end
krauss@21237
   140
      
bulwahn@21131
   141
(* simple depth-first search algorithm for the table *)
bulwahn@22258
   142
bulwahn@21131
   143
fun search_table table =
bulwahn@21131
   144
    case table of
krauss@21237
   145
      [] => SOME []
krauss@21237
   146
    | _ =>
krauss@21237
   147
      let
krauss@21237
   148
        val check_col = forall (fn c => is_Less c orelse is_LessEq c)
krauss@21237
   149
        val col = find_index (check_col) (transpose table)
krauss@21237
   150
      in case col of
krauss@21237
   151
           ~1 => NONE 
krauss@21237
   152
         | _ =>
krauss@21237
   153
           let
krauss@21237
   154
             val order_opt = table |> filter_out (fn x => is_Less (nth x col)) |> map (curry del_index col) |> search_table
krauss@21237
   155
             val transform_order = (fn col => map (fn x => if x>=col then x+1 else x))
krauss@21237
   156
           in case order_opt of
krauss@21237
   157
                NONE => NONE
krauss@21237
   158
              | SOME order =>SOME (col::(transform_order col order))
krauss@21237
   159
           end
krauss@21237
   160
      end
bulwahn@22258
   161
bulwahn@22258
   162
bulwahn@22258
   163
(* find all positions of elements in a list *) 
bulwahn@22258
   164
fun find_index_list pred =
bulwahn@22258
   165
  let fun find _ [] = []
bulwahn@22258
   166
        | find n (x :: xs) = if pred x then n::(find (n + 1) xs) else find (n + 1) xs;
bulwahn@22258
   167
  in find 0 end;
bulwahn@22258
   168
bulwahn@22258
   169
(* simple breadth-first search algorithm for the table *) 
bulwahn@22258
   170
(*
bulwahn@22258
   171
fun bfs_search_table tables =
bulwahn@22258
   172
    case tables of
bulwahn@22258
   173
      [] => sys_error "INTERNAL ERROR IN lexicographic order termination tactic - fun search_table (breadth search finished)" 
bulwahn@22258
   174
    | (table::rtables) => let
bulwahn@22258
   175
        val check_col = forall (fn c => is_Less c orelse is_LessEq c)
bulwahn@22258
   176
        val cols = find_index_list (check_col) (transpose table)
bulwahn@22258
   177
	val _ = print "table"
bulwahn@22258
   178
	val _ = print table
bulwahn@22258
   179
	val _ = print "possible columns:"
bulwahn@22258
   180
	val _ = print cols
bulwahn@22258
   181
      in case cols of
bulwahn@22258
   182
	[] => NONE
bulwahn@22258
   183
      | _ => let 
bulwahn@22258
   184
	val s =
bulwahn@22258
   185
          map (fn f => f table) (map (fn col => filter_out (fn x => is_Less (nth x col)) |> map (curry del_index col)) cols)
bulwahn@22258
   186
          |> append rtables
bulwahn@22258
   187
        val _ = print s 
bulwahn@22258
   188
        in SOME [1]
bulwahn@22258
   189
        end
bulwahn@22258
   190
      end
bulwahn@22258
   191
bulwahn@22258
   192
fun search_table table = let val _ = bfs_search_table [table] in SOME [1] end
bulwahn@22258
   193
*) 	       
bulwahn@22258
   194
bulwahn@21131
   195
fun prove_row row (st : thm) =
bulwahn@21131
   196
    case row of
krauss@21237
   197
      [] => sys_error "INTERNAL ERROR IN lexicographic order termination tactic - fun prove_row (row is empty)" 
krauss@21237
   198
    | cell::tail =>
krauss@21237
   199
      case cell of
krauss@21237
   200
        Less less_thm =>
krauss@21237
   201
        let
krauss@21237
   202
          val next_thm = st |> SINGLE (rtac measures_less 1) |> the
krauss@21237
   203
        in
krauss@21237
   204
          implies_elim next_thm less_thm
krauss@21237
   205
        end
krauss@21237
   206
      | LessEq lesseq_thm =>
krauss@21237
   207
        let
krauss@21237
   208
          val next_thm = st |> SINGLE (rtac measures_lesseq 1) |> the
krauss@21237
   209
        in
krauss@21237
   210
          implies_elim next_thm lesseq_thm 
krauss@21237
   211
          |> prove_row tail
krauss@21237
   212
        end
krauss@21237
   213
      | _ => sys_error "INTERNAL ERROR IN lexicographic order termination tactic - fun prove_row (Only expecting Less or LessEq)"
krauss@21237
   214
             
bulwahn@21131
   215
fun pr_unprovable_subgoals table =
bulwahn@21131
   216
    filter (fn x => not (is_Less x) andalso not (is_LessEq x)) (flat table)
krauss@21237
   217
    |> map ((fn th => Pretty.string_of (Pretty.chunks (Display.pretty_goals (Thm.nprems_of th) th))) o thm_of_cell)
krauss@21237
   218
    
bulwahn@21131
   219
fun pr_goal thy t i = 
bulwahn@21131
   220
    let
krauss@21237
   221
      val (_, prems, lhs, rhs, _) = dest_term t 
krauss@21237
   222
      val prterm = string_of_cterm o (cterm_of thy)
bulwahn@21131
   223
    in
krauss@21237
   224
      (* also show prems? *)
bulwahn@21131
   225
        i ^ ") " ^ (prterm lhs) ^ " '<' " ^ (prterm rhs) 
bulwahn@21131
   226
    end
krauss@21237
   227
    
bulwahn@21131
   228
fun pr_fun thy t i =
bulwahn@21131
   229
    (string_of_int i) ^ ") " ^ (string_of_cterm (cterm_of thy t))
krauss@21237
   230
    
bulwahn@21131
   231
fun pr_cell cell = case cell of Less _ => " <  " 
krauss@21237
   232
                              | LessEq _ => " <= " 
krauss@21237
   233
                              | None _ => " N  "
krauss@21237
   234
                              | False _ => " F  "
krauss@21237
   235
                                             
bulwahn@21131
   236
(* fun pr_err: prints the table if tactic failed *)
bulwahn@22258
   237
fun pr_err table thy tl measure_funs =  
bulwahn@21131
   238
    let 
krauss@21237
   239
      val gc = map (fn i => chr (i + 96)) (1 upto (length table))
bulwahn@22258
   240
      val mc = 1 upto (length measure_funs)
krauss@21237
   241
      val tstr = ("   " ^ (concat (map (fn i => " " ^ (string_of_int i) ^ "  ") mc))) ::
krauss@21237
   242
                 (map2 (fn r => fn i => i ^ ": " ^ (concat (map pr_cell r))) table gc)
krauss@21237
   243
      val gstr = ("Goals:"::(map2 (pr_goal thy) tl gc))
bulwahn@22258
   244
      val mstr = ("Measures:"::(map2 (pr_fun thy) measure_funs mc))   
krauss@21237
   245
      val ustr = ("Unfinished subgoals:"::(pr_unprovable_subgoals table))
bulwahn@21131
   246
    in
krauss@21237
   247
      tstr @ gstr @ mstr @ ustr
bulwahn@21131
   248
    end
krauss@21237
   249
      
bulwahn@21131
   250
(* the main function: create table, search table, create relation,
wenzelm@21590
   251
   and prove the subgoals *)  (* FIXME proper goal addressing -- do not hardwire 1 *)
krauss@21319
   252
fun lexicographic_order_tac ctxt (st: thm) = 
bulwahn@21131
   253
    let
krauss@21237
   254
      val thy = theory_of_thm st
krauss@21319
   255
      val termination_thm = the (FundefCommon.get_termination_rule ctxt)
krauss@21237
   256
      val next_st = SINGLE (rtac termination_thm 1) st |> the
krauss@21237
   257
      val premlist = prems_of next_st
bulwahn@21131
   258
    in
krauss@21237
   259
      case premlist of 
bulwahn@21131
   260
            [] => error "invalid number of subgoals for this tactic - expecting at least 1 subgoal" 
bulwahn@21131
   261
          | (wf::tl) => let
krauss@21237
   262
    val (var, prop) = FundefLib.dest_all wf
krauss@21237
   263
    val rel = HOLogic.dest_Trueprop prop |> dest_wf |> head_of
krauss@21237
   264
    val crel = cterm_of thy rel
bulwahn@22258
   265
    val measure_funs = mk_all_measure_funs (fastype_of var)
krauss@21237
   266
    val _ = writeln "Creating table"
bulwahn@22258
   267
    val table = map (mk_row thy measure_funs) tl
krauss@21237
   268
    val _ = writeln "Searching for lexicographic order"
krauss@21237
   269
    val possible_order = search_table table
krauss@21237
   270
      in
krauss@21237
   271
    case possible_order of 
bulwahn@22258
   272
        NONE => error (cat_lines ("Could not find lexicographic termination order:"::(pr_err table thy tl measure_funs)))
krauss@21237
   273
      | SOME order  => let
krauss@21237
   274
      val clean_table = map (fn x => map (nth x) order) table
bulwahn@22258
   275
      val funs = map (nth measure_funs) order
wenzelm@21757
   276
      val list = HOLogic.mk_list (fastype_of var --> HOLogic.natT) funs
krauss@21237
   277
      val relterm = Abs ("x", fastype_of var, Const(measures, (fastype_of list) --> (range_type (fastype_of rel))) $ list)
krauss@21237
   278
      val crelterm = cterm_of thy relterm
krauss@21237
   279
      val _ = writeln ("Instantiating R with " ^ (string_of_cterm crelterm))
krauss@21237
   280
      val _ = writeln "Proving subgoals"
krauss@21237
   281
        in
krauss@21237
   282
      next_st |> cterm_instantiate [(crel, crelterm)]
krauss@21237
   283
        |> SINGLE (rtac wf_measures 1) |> the
krauss@21237
   284
        |> fold prove_row clean_table
krauss@21237
   285
        |> Seq.single
bulwahn@21131
   286
                    end
bulwahn@21131
   287
            end
bulwahn@21131
   288
    end
bulwahn@21131
   289
krauss@21319
   290
val lexicographic_order = Method.SIMPLE_METHOD o lexicographic_order_tac
krauss@21201
   291
krauss@21319
   292
val setup = Method.add_methods [("lexicographic_order", Method.ctxt_args lexicographic_order, "termination prover for lexicographic orderings")]
bulwahn@21131
   293
wenzelm@21590
   294
end