src/HOL/Tools/Metis/metis_tactic.ML
author blanchet
Thu, 19 Jan 2012 21:37:12 +0100
changeset 47129 e2e52c7d25c9
parent 46754 cf7ef3fca5e4
child 47148 0b8b73b49848
permissions -rw-r--r--
renamed "sound" option to "strict"
blanchet@45515
     1
(*  Title:      HOL/Tools/Metis/metis_tactic.ML
blanchet@38261
     2
    Author:     Kong W. Susanto, Cambridge University Computer Laboratory
blanchet@38261
     3
    Author:     Lawrence C. Paulson, Cambridge University Computer Laboratory
blanchet@38261
     4
    Author:     Jasmin Blanchette, TU Muenchen
wenzelm@23442
     5
    Copyright   Cambridge University 2007
wenzelm@23447
     6
wenzelm@29266
     7
HOL setup for the Metis prover.
wenzelm@23442
     8
*)
wenzelm@23442
     9
blanchet@45515
    10
signature METIS_TACTIC =
wenzelm@23442
    11
sig
blanchet@40160
    12
  val trace : bool Config.T
blanchet@40913
    13
  val verbose : bool Config.T
blanchet@40072
    14
  val new_skolemizer : bool Config.T
blanchet@45805
    15
  val type_has_top_sort : typ -> bool
blanchet@46385
    16
  val metis_tac :
blanchet@46385
    17
    string list -> string -> Proof.context -> thm list -> int -> tactic
blanchet@46392
    18
  val metis_lam_transs : string list
blanchet@46390
    19
  val parse_metis_options : (string list option * string option) parser
blanchet@39737
    20
  val setup : theory -> theory
wenzelm@23442
    21
end
wenzelm@23442
    22
blanchet@45515
    23
structure Metis_Tactic : METIS_TACTIC =
wenzelm@23442
    24
struct
wenzelm@23442
    25
blanchet@43926
    26
open ATP_Translate
blanchet@46390
    27
open ATP_Reconstruct
blanchet@39734
    28
open Metis_Translate
blanchet@39737
    29
open Metis_Reconstruct
blanchet@35826
    30
blanchet@43930
    31
val new_skolemizer =
blanchet@43930
    32
  Attrib.setup_config_bool @{binding metis_new_skolemizer} (K false)
wenzelm@23442
    33
blanchet@43975
    34
(* Designed to work also with monomorphic instances of polymorphic theorems. *)
blanchet@39737
    35
fun have_common_thm ths1 ths2 =
blanchet@44172
    36
  exists (member (Term.aconv_untyped o pairself prop_of) ths1)
blanchet@43975
    37
         (map Meson.make_meta_clause ths2)
wenzelm@32956
    38
wenzelm@32956
    39
(*Determining which axiom clauses are actually used*)
blanchet@39665
    40
fun used_axioms axioms (th, Metis_Proof.Axiom _) = SOME (lookth axioms th)
blanchet@43969
    41
  | used_axioms _ _ = NONE
wenzelm@32956
    42
blanchet@43970
    43
(* Lightweight predicate type information comes in two flavors, "t = t'" and
blanchet@43970
    44
   "t => t'", where "t" and "t'" are the same term modulo type tags.
blanchet@43970
    45
   In Isabelle, type tags are stripped away, so we are left with "t = t" or
blanchet@44000
    46
   "t => t". Type tag idempotence is also handled this way. *)
blanchet@46379
    47
fun reflexive_or_trivial_from_metis ctxt type_enc sym_tab concealed mth =
blanchet@43977
    48
  let val thy = Proof_Context.theory_of ctxt in
blanchet@46379
    49
    case hol_clause_from_metis ctxt type_enc sym_tab concealed mth of
blanchet@43977
    50
      Const (@{const_name HOL.eq}, _) $ _ $ t =>
blanchet@45267
    51
      let
blanchet@45267
    52
        val ct = cterm_of thy t
blanchet@45267
    53
        val cT = ctyp_of_term ct
blanchet@45267
    54
      in refl |> Drule.instantiate' [SOME cT] [SOME ct] end
blanchet@43977
    55
    | Const (@{const_name disj}, _) $ t1 $ t2 =>
blanchet@43977
    56
      (if can HOLogic.dest_not t1 then t2 else t1)
blanchet@43977
    57
      |> HOLogic.mk_Trueprop |> cterm_of thy |> Thm.trivial
blanchet@46382
    58
    | _ => raise Fail "expected reflexive or trivial clause"
blanchet@43977
    59
  end
blanchet@43970
    60
  |> Meson.make_meta_clause
blanchet@43970
    61
blanchet@46440
    62
fun lam_lifted_from_metis ctxt type_enc sym_tab concealed mth =
blanchet@46382
    63
  let
blanchet@46382
    64
    val thy = Proof_Context.theory_of ctxt
blanchet@46382
    65
    val tac = rewrite_goals_tac @{thms lambda_def_raw} THEN rtac refl 1
blanchet@46382
    66
    val t = hol_clause_from_metis ctxt type_enc sym_tab concealed mth
blanchet@46382
    67
    val ct = cterm_of thy (HOLogic.mk_Trueprop t)
blanchet@46382
    68
  in Goal.prove_internal [] ct (K tac) |> Meson.make_meta_clause end
blanchet@46382
    69
blanchet@46441
    70
fun add_vars_and_frees (t $ u) = fold (add_vars_and_frees) [t, u]
blanchet@46441
    71
  | add_vars_and_frees (Abs (_, _, t)) = add_vars_and_frees t
blanchet@46441
    72
  | add_vars_and_frees (t as Var _) = insert (op =) t
blanchet@46441
    73
  | add_vars_and_frees (t as Free _) = insert (op =) t
blanchet@46441
    74
  | add_vars_and_frees _ = I
blanchet@46441
    75
blanchet@46440
    76
fun introduce_lam_wrappers ctxt th =
blanchet@46382
    77
  if Meson_Clausify.is_quasi_lambda_free (prop_of th) then
blanchet@46382
    78
    th
blanchet@46382
    79
  else
blanchet@46382
    80
    let
blanchet@46441
    81
      val thy = Proof_Context.theory_of ctxt
blanchet@46441
    82
      fun conv first ctxt ct =
blanchet@46382
    83
        if Meson_Clausify.is_quasi_lambda_free (term_of ct) then
blanchet@46382
    84
          Thm.reflexive ct
blanchet@46382
    85
        else case term_of ct of
blanchet@46754
    86
          Abs (_, _, u) =>
blanchet@46441
    87
          if first then
blanchet@46441
    88
            case add_vars_and_frees u [] of
blanchet@46441
    89
              [] =>
blanchet@46441
    90
              Conv.abs_conv (conv false o snd) ctxt ct
blanchet@46441
    91
              |> (fn th => Meson.first_order_resolve th @{thm Metis.eq_lambdaI})
blanchet@46441
    92
            | v :: _ =>
blanchet@46441
    93
              Abs (Name.uu, fastype_of v, abstract_over (v, term_of ct)) $ v
blanchet@46441
    94
              |> cterm_of thy
blanchet@46441
    95
              |> Conv.comb_conv (conv true ctxt)
blanchet@46441
    96
          else
blanchet@46441
    97
            Conv.abs_conv (conv false o snd) ctxt ct
blanchet@46441
    98
        | Const (@{const_name Meson.skolem}, _) $ _ => Thm.reflexive ct
blanchet@46382
    99
        | _ => Conv.comb_conv (conv true ctxt) ct
blanchet@46441
   100
      val eq_th = conv true ctxt (cprop_of th)
blanchet@46441
   101
      (* We replace the equation's left-hand side with a beta-equivalent term
blanchet@46441
   102
         so that "Thm.equal_elim" works below. *)
blanchet@46441
   103
      val t0 $ _ $ t2 = prop_of eq_th
blanchet@46441
   104
      val eq_ct = t0 $ prop_of th $ t2 |> cterm_of thy
blanchet@46441
   105
      val eq_th' = Goal.prove_internal [] eq_ct (K (Tactic.rtac eq_th 1))
blanchet@46441
   106
    in Thm.equal_elim eq_th' th end
blanchet@46382
   107
blanchet@45452
   108
val clause_params =
blanchet@39690
   109
  {ordering = Metis_KnuthBendixOrder.default,
blanchet@45347
   110
   orderLiterals = Metis_Clause.UnsignedLiteralOrder,
blanchet@39690
   111
   orderTerms = true}
blanchet@45452
   112
val active_params =
blanchet@45452
   113
  {clause = clause_params,
blanchet@39690
   114
   prefactor = #prefactor Metis_Active.default,
blanchet@39690
   115
   postfactor = #postfactor Metis_Active.default}
blanchet@39690
   116
val waiting_params =
blanchet@39690
   117
  {symbolsWeight = 1.0,
blanchet@39690
   118
   variablesWeight = 0.0,
blanchet@39690
   119
   literalsWeight = 0.0,
blanchet@39690
   120
   models = []}
blanchet@45452
   121
val resolution_params = {active = active_params, waiting = waiting_params}
blanchet@37573
   122
blanchet@37516
   123
(* Main function to start Metis proof and reconstruction *)
blanchet@46390
   124
fun FOL_SOLVE (type_enc :: fallback_type_encs) lam_trans ctxt cls ths0 =
wenzelm@43232
   125
  let val thy = Proof_Context.theory_of ctxt
blanchet@40082
   126
      val new_skolemizer =
blanchet@40131
   127
        Config.get ctxt new_skolemizer orelse null (Meson.choice_theorems thy)
blanchet@46441
   128
      val do_lams = lam_trans = lam_liftingN ? introduce_lam_wrappers ctxt
blanchet@35826
   129
      val th_cls_pairs =
blanchet@40075
   130
        map2 (fn j => fn th =>
blanchet@40075
   131
                (Thm.get_name_hint th,
blanchet@46441
   132
                 th |> Drule.eta_contraction_rule
blanchet@46441
   133
                    |> Meson_Clausify.cnf_axiom ctxt new_skolemizer
blanchet@46441
   134
                                                (lam_trans = combinatorsN) j
blanchet@46441
   135
                    ||> map do_lams))
blanchet@40075
   136
             (0 upto length ths0 - 1) ths0
blanchet@43933
   137
      val ths = maps (snd o snd) th_cls_pairs
blanchet@40119
   138
      val dischargers = map (fst o snd) th_cls_pairs
blanchet@46441
   139
      val cls = cls |> map (Drule.eta_contraction_rule #> do_lams)
blanchet@40159
   140
      val _ = trace_msg ctxt (fn () => "FOL_SOLVE: CONJECTURE CLAUSES")
blanchet@40159
   141
      val _ = app (fn th => trace_msg ctxt (fn () => Display.string_of_thm ctxt th)) cls
blanchet@45270
   142
      val _ = trace_msg ctxt (fn () => "type_enc = " ^ type_enc)
blanchet@47129
   143
      val type_enc = type_enc_from_string Strict type_enc
blanchet@46440
   144
      val (sym_tab, axioms, concealed) =
blanchet@46385
   145
        prepare_metis_problem ctxt type_enc lam_trans cls ths
blanchet@44000
   146
      fun get_isa_thm mth Isa_Reflexive_or_Trivial =
blanchet@46379
   147
          reflexive_or_trivial_from_metis ctxt type_enc sym_tab concealed mth
blanchet@46382
   148
        | get_isa_thm mth Isa_Lambda_Lifted =
blanchet@46440
   149
          lam_lifted_from_metis ctxt type_enc sym_tab concealed mth
blanchet@46440
   150
        | get_isa_thm _ (Isa_Raw ith) = ith
blanchet@46440
   151
      val axioms = axioms |> map (fn (mth, ith) => (mth, get_isa_thm mth ith))
blanchet@46430
   152
      val _ = trace_msg ctxt (fn () => "ISABELLE CLAUSES")
blanchet@46430
   153
      val _ = app (fn (_, ith) => trace_msg ctxt (fn () => Display.string_of_thm ctxt ith)) axioms
blanchet@46430
   154
      val _ = trace_msg ctxt (fn () => "METIS CLAUSES")
blanchet@46430
   155
      val _ = app (fn (mth, _) => trace_msg ctxt (fn () => Metis_Thm.toString mth)) axioms
blanchet@40159
   156
      val _ = trace_msg ctxt (fn () => "START METIS PROVE PROCESS")
wenzelm@32956
   157
  in
blanchet@44000
   158
      case filter (fn t => prop_of t aconv @{prop False}) cls of
blanchet@44000
   159
          false_th :: _ => [false_th RS @{thm FalseE}]
wenzelm@32956
   160
        | [] =>
blanchet@45452
   161
      case Metis_Resolution.new resolution_params
blanchet@46430
   162
                                {axioms = axioms |> map fst, conjecture = []}
blanchet@39737
   163
           |> Metis_Resolution.loop of
blanchet@39665
   164
          Metis_Resolution.Contradiction mth =>
blanchet@40159
   165
            let val _ = trace_msg ctxt (fn () => "METIS RECONSTRUCTION START: " ^
blanchet@39665
   166
                          Metis_Thm.toString mth)
wenzelm@32956
   167
                val ctxt' = fold Variable.declare_constraints (map prop_of cls) ctxt
wenzelm@32956
   168
                             (*add constraints arising from converting goal to clause form*)
blanchet@39665
   169
                val proof = Metis_Proof.proof mth
blanchet@43935
   170
                val result =
blanchet@44053
   171
                  axioms
blanchet@46379
   172
                  |> fold (replay_one_inference ctxt' type_enc concealed sym_tab) proof
blanchet@46440
   173
                val used = proof |> map_filter (used_axioms axioms)
blanchet@40159
   174
                val _ = trace_msg ctxt (fn () => "METIS COMPLETED...clauses actually used:")
blanchet@40159
   175
                val _ = app (fn th => trace_msg ctxt (fn () => Display.string_of_thm ctxt th)) used
blanchet@43975
   176
                val names = th_cls_pairs |> map fst
blanchet@43975
   177
                val used_names =
blanchet@43975
   178
                  th_cls_pairs
blanchet@43975
   179
                  |> map_filter (fn (name, (_, cls)) =>
blanchet@43975
   180
                                    if have_common_thm used cls then SOME name
blanchet@43975
   181
                                    else NONE)
blanchet@43975
   182
                val unused_names = names |> subtract (op =) used_names
wenzelm@32956
   183
            in
blanchet@39737
   184
                if not (null cls) andalso not (have_common_thm used cls) then
blanchet@43521
   185
                  verbose_warning ctxt "The assumptions are inconsistent"
blanchet@36383
   186
                else
blanchet@36383
   187
                  ();
blanchet@43975
   188
                if not (null unused_names) then
blanchet@43975
   189
                  "Unused theorems: " ^ commas_quote unused_names
blanchet@43975
   190
                  |> verbose_warning ctxt
blanchet@36230
   191
                else
blanchet@36230
   192
                  ();
wenzelm@32956
   193
                case result of
wenzelm@32956
   194
                    (_,ith)::_ =>
blanchet@40159
   195
                        (trace_msg ctxt (fn () => "Success: " ^ Display.string_of_thm ctxt ith);
blanchet@40068
   196
                         [discharge_skolem_premises ctxt dischargers ith])
blanchet@40159
   197
                  | _ => (trace_msg ctxt (fn () => "Metis: No result"); [])
wenzelm@32956
   198
            end
blanchet@39665
   199
        | Metis_Resolution.Satisfiable _ =>
blanchet@40159
   200
            (trace_msg ctxt (fn () => "Metis: No first-order proof with the lemmas supplied");
blanchet@46390
   201
             if null fallback_type_encs then
blanchet@43875
   202
               ()
blanchet@43875
   203
             else
blanchet@43521
   204
               raise METIS ("FOL_SOLVE",
blanchet@43875
   205
                            "No first-order proof with the lemmas supplied");
blanchet@38343
   206
             [])
blanchet@43598
   207
  end
blanchet@43598
   208
  handle METIS (loc, msg) =>
blanchet@46390
   209
         case fallback_type_encs of
blanchet@43875
   210
           [] => error ("Failed to replay Metis proof in Isabelle." ^
blanchet@43875
   211
                        (if Config.get ctxt verbose then "\n" ^ loc ^ ": " ^ msg
blanchet@43875
   212
                         else ""))
blanchet@46390
   213
         | first_fallback :: _ =>
blanchet@44069
   214
           (verbose_warning ctxt
blanchet@44069
   215
                ("Falling back on " ^
blanchet@46390
   216
                 quote (metis_call first_fallback lam_trans) ^ "...");
blanchet@46390
   217
            FOL_SOLVE fallback_type_encs lam_trans ctxt cls ths0)
wenzelm@23442
   218
blanchet@46379
   219
fun neg_clausify ctxt combinators =
blanchet@38262
   220
  single
blanchet@44835
   221
  #> Meson.make_clauses_unsorted ctxt
blanchet@46379
   222
  #> combinators ? map Meson_Clausify.introduce_combinators_in_theorem
blanchet@38262
   223
  #> Meson.finish_cnf
blanchet@38262
   224
blanchet@39496
   225
fun preskolem_tac ctxt st0 =
blanchet@39496
   226
  (if exists (Meson.has_too_many_clauses ctxt)
blanchet@39496
   227
             (Logic.prems_of_goal (prop_of st0) 1) then
blanchet@43207
   228
     Simplifier.full_simp_tac (Meson_Clausify.ss_only @{thms not_all not_ex}) 1
blanchet@43207
   229
     THEN cnf.cnfx_rewrite_tac ctxt 1
blanchet@39496
   230
   else
blanchet@39496
   231
     all_tac) st0
blanchet@39496
   232
blanchet@38890
   233
val type_has_top_sort =
blanchet@38890
   234
  exists_subtype (fn TFree (_, []) => true | TVar (_, []) => true | _ => false)
blanchet@38890
   235
blanchet@46390
   236
fun generic_metis_tac type_encs lam_trans ctxt ths i st0 =
blanchet@38166
   237
  let
blanchet@40159
   238
    val _ = trace_msg ctxt (fn () =>
blanchet@44035
   239
        "Metis called with theorems\n" ^
blanchet@43875
   240
        cat_lines (map (Display.string_of_thm ctxt) ths))
blanchet@46390
   241
    val type_encs = type_encs |> maps unalias_type_enc
blanchet@46379
   242
    fun tac clause =
blanchet@46390
   243
      resolve_tac (FOL_SOLVE type_encs lam_trans ctxt clause ths) 1
wenzelm@32956
   244
  in
blanchet@37626
   245
    if exists_type type_has_top_sort (prop_of st0) then
blanchet@44170
   246
      verbose_warning ctxt "Proof state contains the universal sort {}"
wenzelm@35568
   247
    else
blanchet@44170
   248
      ();
blanchet@46379
   249
    Meson.MESON (preskolem_tac ctxt)
blanchet@46385
   250
        (maps (neg_clausify ctxt (lam_trans = combinatorsN))) tac ctxt i st0
wenzelm@32956
   251
  end
wenzelm@23442
   252
blanchet@46390
   253
fun metis_tac [] = generic_metis_tac partial_type_encs
blanchet@46390
   254
  | metis_tac type_encs = generic_metis_tac type_encs
wenzelm@23442
   255
blanchet@38855
   256
(* Whenever "X" has schematic type variables, we treat "using X by metis" as
blanchet@43941
   257
   "by (metis X)" to prevent "Subgoal.FOCUS" from freezing the type variables.
blanchet@38855
   258
   We don't do it for nonschematic facts "X" because this breaks a few proofs
blanchet@38855
   259
   (in the rare and subtle case where a proof relied on extensionality not being
blanchet@39238
   260
   applied) and brings few benefits. *)
blanchet@38855
   261
val has_tvar =
blanchet@38855
   262
  exists_type (exists_subtype (fn TVar _ => true | _ => false)) o prop_of
blanchet@43875
   263
blanchet@46390
   264
fun method default_type_encs ((override_type_encs, lam_trans), ths) ctxt facts =
blanchet@43941
   265
  let
blanchet@44069
   266
    val _ =
blanchet@46390
   267
      if default_type_encs = full_type_encs then
wenzelm@44923
   268
        legacy_feature "Old \"metisFT\" method -- use \"metis (full_types)\" instead"
blanchet@44069
   269
      else
blanchet@44069
   270
        ()
blanchet@43941
   271
    val (schem_facts, nonschem_facts) = List.partition has_tvar facts
blanchet@46390
   272
    val type_encs = override_type_encs |> the_default default_type_encs
blanchet@46390
   273
    val lam_trans = lam_trans |> the_default metis_default_lam_trans
blanchet@43941
   274
  in
blanchet@43940
   275
    HEADGOAL (Method.insert_tac nonschem_facts THEN'
blanchet@46390
   276
              CHANGED_PROP o generic_metis_tac type_encs lam_trans ctxt
blanchet@46385
   277
                                               (schem_facts @ ths))
blanchet@43940
   278
  end
blanchet@43941
   279
blanchet@46392
   280
val metis_lam_transs = [hide_lamsN, lam_liftingN, combinatorsN]
blanchet@46385
   281
blanchet@46449
   282
fun set_opt _ x NONE = SOME x
blanchet@46449
   283
  | set_opt get x (SOME x0) =
blanchet@46449
   284
    error ("Cannot specify both " ^ quote (get x0) ^ " and " ^ quote (get x) ^
blanchet@46449
   285
           ".")
blanchet@46390
   286
fun consider_opt s =
blanchet@46449
   287
  if member (op =) metis_lam_transs s then apsnd (set_opt I s)
blanchet@46449
   288
  else apfst (set_opt hd [s])
blanchet@46390
   289
blanchet@46390
   290
val parse_metis_options =
blanchet@46390
   291
  Scan.optional
blanchet@46390
   292
      (Args.parens (Parse.short_ident
blanchet@46390
   293
                    -- Scan.option (Parse.$$$ "," |-- Parse.short_ident))
blanchet@46390
   294
       >> (fn (s, s') =>
blanchet@46390
   295
              (NONE, NONE) |> consider_opt s
blanchet@46390
   296
                           |> (case s' of SOME s' => consider_opt s' | _ => I)))
blanchet@46390
   297
      (NONE, NONE)
blanchet@46390
   298
blanchet@46390
   299
fun setup_method (binding, type_encs) =
blanchet@46390
   300
  Scan.lift parse_metis_options -- Attrib.thms >> (METHOD oo method type_encs)
blanchet@44069
   301
  |> Method.setup binding
wenzelm@23442
   302
wenzelm@32956
   303
val setup =
blanchet@46390
   304
  [((@{binding metis}, partial_type_encs),
blanchet@44069
   305
    "Metis for FOL and HOL problems"),
blanchet@46390
   306
   ((@{binding metisFT}, full_type_encs),
blanchet@44053
   307
    "Metis for FOL/HOL problems with fully-typed translation")]
blanchet@43875
   308
  |> fold (uncurry setup_method)
wenzelm@23442
   309
wenzelm@23442
   310
end;