src/Provers/Arith/fast_lin_arith.ML
changeset 24076 ae946f751c44
parent 24039 273698405054
child 24112 6c4e7d17f9b0
equal deleted inserted replaced
24075:366d4d234814 24076:ae946f751c44
     1 (*  Title:      Provers/Arith/fast_lin_arith.ML
     1 (*  Title:      Provers/Arith/fast_lin_arith.ML
     2     ID:         $Id$
     2     ID:         $Id$
     3     Author:     Tobias Nipkow
     3     Author:     Tobias Nipkow and Tjark Weber
     4     Copyright   1998  TU Munich
     4 
     5 
     5 A generic linear arithmetic package.  It provides two tactics
     6 A generic linear arithmetic package.
     6 (cut_lin_arith_tac, lin_arith_tac) and a simplification procedure
     7 It provides two tactics
     7 (lin_arith_simproc).
     8 
     8 
     9     lin_arith_tac:         int -> tactic
     9 Only take premises and conclusions into account that are already
    10 cut_lin_arith_tac: thms -> int -> tactic
    10 (negated) (in)equations. lin_arith_simproc tries to prove or disprove
    11 
    11 the term.
    12 and a simplification procedure
       
    13 
       
    14     lin_arith_prover: theory -> simpset -> term -> thm option
       
    15 
       
    16 Only take premises and conclusions into account that are already (negated)
       
    17 (in)equations. lin_arith_prover tries to prove or disprove the term.
       
    18 *)
    12 *)
    19 
    13 
    20 (* Debugging: set Fast_Arith.trace *)
    14 (* Debugging: set Fast_Arith.trace *)
    21 
    15 
    22 (*** Data needed for setting up the linear arithmetic package ***)
    16 (*** Data needed for setting up the linear arithmetic package ***)
    52 mk_nat_thm(t) = "0 <= t"
    46 mk_nat_thm(t) = "0 <= t"
    53 *)
    47 *)
    54 
    48 
    55 signature LIN_ARITH_DATA =
    49 signature LIN_ARITH_DATA =
    56 sig
    50 sig
    57   (* internal representation of linear (in-)equations: *)
    51   (*internal representation of linear (in-)equations:*)
    58   type decompT = (term * Rat.rat) list * Rat.rat * string * (term * Rat.rat) list * Rat.rat * bool
    52   type decompT = (term * Rat.rat) list * Rat.rat * string * (term * Rat.rat) list * Rat.rat * bool
    59   val decomp: theory -> term -> decompT option
    53   val decomp: Proof.context -> term -> decompT option
    60   val domain_is_nat : term -> bool
    54   val domain_is_nat: term -> bool
    61   (* preprocessing, performed on a representation of subgoals as list of premises: *)
    55 
    62   val pre_decomp: theory -> typ list * term list -> (typ list * term list) list
    56   (*preprocessing, performed on a representation of subgoals as list of premises:*)
    63   (* preprocessing, performed on the goal -- must do the same as 'pre_decomp': *)
    57   val pre_decomp: Proof.context -> typ list * term list -> (typ list * term list) list
    64   val pre_tac   : int -> Tactical.tactic
    58 
    65   val number_of : IntInf.int * typ -> term
    59   (*preprocessing, performed on the goal -- must do the same as 'pre_decomp':*)
       
    60   val pre_tac: Proof.context -> int -> tactic
       
    61   val number_of: IntInf.int * typ -> term
       
    62 
       
    63   (*the limit on the number of ~= allowed; because each ~= is split
       
    64     into two cases, this can lead to an explosion*)
       
    65   val fast_arith_neq_limit: int ConfigOption.T
    66 end;
    66 end;
    67 (*
    67 (*
    68 decomp(`x Rel y') should yield (p,i,Rel,q,j,d)
    68 decomp(`x Rel y') should yield (p,i,Rel,q,j,d)
    69    where Rel is one of "<", "~<", "<=", "~<=" and "=" and
    69    where Rel is one of "<", "~<", "<=", "~<=" and "=" and
    70          p (q, respectively) is the decomposition of the sum term x
    70          p (q, respectively) is the decomposition of the sum term x
    90 sig
    90 sig
    91   val map_data: ({add_mono_thms: thm list, mult_mono_thms: thm list, inj_thms: thm list,
    91   val map_data: ({add_mono_thms: thm list, mult_mono_thms: thm list, inj_thms: thm list,
    92                  lessD: thm list, neqE: thm list, simpset: Simplifier.simpset}
    92                  lessD: thm list, neqE: thm list, simpset: Simplifier.simpset}
    93                  -> {add_mono_thms: thm list, mult_mono_thms: thm list, inj_thms: thm list,
    93                  -> {add_mono_thms: thm list, mult_mono_thms: thm list, inj_thms: thm list,
    94                      lessD: thm list, neqE: thm list, simpset: Simplifier.simpset})
    94                      lessD: thm list, neqE: thm list, simpset: Simplifier.simpset})
    95                 -> theory -> theory
    95                 -> Context.generic -> Context.generic
    96   val trace: bool ref
    96   val trace: bool ref
    97   val fast_arith_neq_limit: int ref
       
    98   val lin_arith_prover: theory -> simpset -> term -> thm option
       
    99   val     lin_arith_tac:    bool -> int -> tactic
       
   100   val cut_lin_arith_tac: simpset -> int -> tactic
    97   val cut_lin_arith_tac: simpset -> int -> tactic
       
    98   val lin_arith_tac: Proof.context -> bool -> int -> tactic
       
    99   val lin_arith_simproc: simpset -> term -> thm option
   101 end;
   100 end;
   102 
   101 
   103 functor Fast_Lin_Arith(structure LA_Logic:LIN_ARITH_LOGIC
   102 functor Fast_Lin_Arith
   104                        and       LA_Data:LIN_ARITH_DATA) : FAST_LIN_ARITH =
   103   (structure LA_Logic: LIN_ARITH_LOGIC and LA_Data: LIN_ARITH_DATA): FAST_LIN_ARITH =
   105 struct
   104 struct
   106 
   105 
   107 
   106 
   108 (** theory data **)
   107 (** theory data **)
   109 
   108 
   110 structure Data = TheoryDataFun
   109 structure Data = GenericDataFun
   111 (
   110 (
   112   type T = {add_mono_thms: thm list, mult_mono_thms: thm list, inj_thms: thm list,
   111   type T =
   113             lessD: thm list, neqE: thm list, simpset: Simplifier.simpset};
   112    {add_mono_thms: thm list,
       
   113     mult_mono_thms: thm list,
       
   114     inj_thms: thm list,
       
   115     lessD: thm list,
       
   116     neqE: thm list,
       
   117     simpset: Simplifier.simpset};
   114 
   118 
   115   val empty = {add_mono_thms = [], mult_mono_thms = [], inj_thms = [],
   119   val empty = {add_mono_thms = [], mult_mono_thms = [], inj_thms = [],
   116                lessD = [], neqE = [], simpset = Simplifier.empty_ss};
   120                lessD = [], neqE = [], simpset = Simplifier.empty_ss};
   117   val copy = I;
       
   118   val extend = I;
   121   val extend = I;
   119 
       
   120   fun merge _
   122   fun merge _
   121     ({add_mono_thms= add_mono_thms1, mult_mono_thms= mult_mono_thms1, inj_thms= inj_thms1,
   123     ({add_mono_thms= add_mono_thms1, mult_mono_thms= mult_mono_thms1, inj_thms= inj_thms1,
   122       lessD = lessD1, neqE=neqE1, simpset = simpset1},
   124       lessD = lessD1, neqE=neqE1, simpset = simpset1},
   123      {add_mono_thms= add_mono_thms2, mult_mono_thms= mult_mono_thms2, inj_thms= inj_thms2,
   125      {add_mono_thms= add_mono_thms2, mult_mono_thms= mult_mono_thms2, inj_thms= inj_thms2,
   124       lessD = lessD2, neqE=neqE2, simpset = simpset2}) =
   126       lessD = lessD2, neqE=neqE2, simpset = simpset2}) =
   129      neqE = Thm.merge_thms (neqE1, neqE2),
   131      neqE = Thm.merge_thms (neqE1, neqE2),
   130      simpset = Simplifier.merge_ss (simpset1, simpset2)};
   132      simpset = Simplifier.merge_ss (simpset1, simpset2)};
   131 );
   133 );
   132 
   134 
   133 val map_data = Data.map;
   135 val map_data = Data.map;
       
   136 val get_data = Data.get o Context.Proof;
   134 
   137 
   135 
   138 
   136 
   139 
   137 (*** A fast decision procedure ***)
   140 (*** A fast decision procedure ***)
   138 (*** Code ported from HOL Light ***)
   141 (*** Code ported from HOL Light ***)
   407 
   410 
   408 (* ------------------------------------------------------------------------- *)
   411 (* ------------------------------------------------------------------------- *)
   409 (* Translate back a proof.                                                   *)
   412 (* Translate back a proof.                                                   *)
   410 (* ------------------------------------------------------------------------- *)
   413 (* ------------------------------------------------------------------------- *)
   411 
   414 
   412 fun trace_thm (msg : string) (th : thm) : thm =
   415 fun trace_thm msg th =
   413     (if !trace then (tracing msg; tracing (Display.string_of_thm th)) else (); th);
   416   (if !trace then (tracing msg; tracing (Display.string_of_thm th)) else (); th);
   414 
   417 
   415 fun trace_msg (msg : string) : unit =
   418 fun trace_term ctxt msg t =
   416     if !trace then tracing msg else ();
   419   (if !trace then tracing (cat_lines [msg, ProofContext.string_of_term ctxt t]) else (); t)
       
   420 
       
   421 fun trace_msg msg =
       
   422   if !trace then tracing msg else ();
   417 
   423 
   418 (* FIXME OPTIMIZE!!!! (partly done already)
   424 (* FIXME OPTIMIZE!!!! (partly done already)
   419    Addition/Multiplication need i*t representation rather than t+t+...
   425    Addition/Multiplication need i*t representation rather than t+t+...
   420    Get rid of Mulitplied(2). For Nat LA_Data.number_of should return Suc^n
   426    Get rid of Mulitplied(2). For Nat LA_Data.number_of should return Suc^n
   421    because Numerals are not known early enough.
   427    because Numerals are not known early enough.
   423 Simplification may detect a contradiction 'prematurely' due to type
   429 Simplification may detect a contradiction 'prematurely' due to type
   424 information: n+1 <= 0 is simplified to False and does not need to be crossed
   430 information: n+1 <= 0 is simplified to False and does not need to be crossed
   425 with 0 <= n.
   431 with 0 <= n.
   426 *)
   432 *)
   427 local
   433 local
   428  exception FalseE of thm
   434   exception FalseE of thm
   429 in
   435 in
   430 fun mkthm (sg:theory, ss) (asms:thm list) (just:injust) : thm =
   436 fun mkthm ss asms (just: injust) =
   431   let val {add_mono_thms, mult_mono_thms, inj_thms, lessD, simpset, ...} =
   437   let
   432           Data.get sg;
   438     val ctxt = Simplifier.the_context ss;
   433       val simpset' = Simplifier.inherit_context ss simpset;
   439     val thy = ProofContext.theory_of ctxt;
   434       val atoms = Library.foldl (fn (ats, (lhs,_,_,rhs,_,_)) =>
   440     val {add_mono_thms, mult_mono_thms, inj_thms, lessD, simpset, ...} = get_data ctxt;
       
   441     val simpset' = Simplifier.inherit_context ss simpset;
       
   442     val atoms = Library.foldl (fn (ats, (lhs,_,_,rhs,_,_)) =>
   435                             map fst lhs  union  (map fst rhs  union  ats))
   443                             map fst lhs  union  (map fst rhs  union  ats))
   436                         ([], List.mapPartial (fn thm => if Thm.no_prems thm
   444                         ([], List.mapPartial (fn thm => if Thm.no_prems thm
   437                                               then LA_Data.decomp sg (concl_of thm)
   445                                               then LA_Data.decomp ctxt (Thm.concl_of thm)
   438                                               else NONE) asms)
   446                                               else NONE) asms)
   439 
   447 
   440       fun add2 thm1 thm2 =
   448       fun add2 thm1 thm2 =
   441         let val conj = thm1 RS (thm2 RS LA_Logic.conjI)
   449         let val conj = thm1 RS (thm2 RS LA_Logic.conjI)
   442         in get_first (fn th => SOME(conj RS th) handle THM _ => NONE) add_mono_thms
   450         in get_first (fn th => SOME(conj RS th) handle THM _ => NONE) add_mono_thms
   463       fun multn2(n,thm) =
   471       fun multn2(n,thm) =
   464         let val SOME(mth) =
   472         let val SOME(mth) =
   465               get_first (fn th => SOME(thm RS th) handle THM _ => NONE) mult_mono_thms
   473               get_first (fn th => SOME(thm RS th) handle THM _ => NONE) mult_mono_thms
   466             fun cvar(th,_ $ (_ $ _ $ var)) = cterm_of (Thm.theory_of_thm th) var;
   474             fun cvar(th,_ $ (_ $ _ $ var)) = cterm_of (Thm.theory_of_thm th) var;
   467             val cv = cvar(mth, hd(prems_of mth));
   475             val cv = cvar(mth, hd(prems_of mth));
   468             val ct = cterm_of sg (LA_Data.number_of(n,#T(rep_cterm cv)))
   476             val ct = cterm_of thy (LA_Data.number_of(n,#T(rep_cterm cv)))
   469         in instantiate ([],[(cv,ct)]) mth end
   477         in instantiate ([],[(cv,ct)]) mth end
   470 
   478 
   471       fun simp thm =
   479       fun simp thm =
   472         let val thm' = trace_thm "Simplified:" (full_simplify simpset' thm)
   480         let val thm' = trace_thm "Simplified:" (full_simplify simpset' thm)
   473         in if LA_Logic.is_False thm' then raise FalseE thm' else thm' end
   481         in if LA_Logic.is_False thm' then raise FalseE thm' else thm' end
   474 
   482 
   475       fun mk (Asm i)              = trace_thm ("Asm " ^ Int.toString i) (nth asms i)
   483       fun mk (Asm i) = trace_thm ("Asm " ^ string_of_int i) (nth asms i)
   476         | mk (Nat i)              = trace_thm ("Nat " ^ Int.toString i) (LA_Logic.mk_nat_thm sg (nth atoms i))
   484         | mk (Nat i) = trace_thm ("Nat " ^ string_of_int i) (LA_Logic.mk_nat_thm thy (nth atoms i))
   477         | mk (LessD j)            = trace_thm "L" (hd ([mk j] RL lessD))
   485         | mk (LessD j)            = trace_thm "L" (hd ([mk j] RL lessD))
   478         | mk (NotLeD j)           = trace_thm "NLe" (mk j RS LA_Logic.not_leD)
   486         | mk (NotLeD j)           = trace_thm "NLe" (mk j RS LA_Logic.not_leD)
   479         | mk (NotLeDD j)          = trace_thm "NLeD" (hd ([mk j RS LA_Logic.not_leD] RL lessD))
   487         | mk (NotLeDD j)          = trace_thm "NLeD" (hd ([mk j RS LA_Logic.not_leD] RL lessD))
   480         | mk (NotLessD j)         = trace_thm "NL" (mk j RS LA_Logic.not_lessD)
   488         | mk (NotLessD j)         = trace_thm "NL" (mk j RS LA_Logic.not_lessD)
   481         | mk (Added (j1, j2))     = simp (trace_thm "+" (addthms (mk j1) (mk j2)))
   489         | mk (Added (j1, j2))     = simp (trace_thm "+" (addthms (mk j1) (mk j2)))
   548   curry (op ~~) sds
   556   curry (op ~~) sds
   549   #> map print_atom
   557   #> map print_atom
   550   #> commas
   558   #> commas
   551   #> curry (op ^) "Counterexample (possibly spurious):\n";
   559   #> curry (op ^) "Counterexample (possibly spurious):\n";
   552 
   560 
   553 fun trace_ex (sg, params, atoms, discr, n, hist : history) =
   561 fun trace_ex ctxt params atoms discr n (hist: history) =
   554   case hist of
   562   case hist of
   555     [] => ()
   563     [] => ()
   556   | (v, lineqs) :: hist' =>
   564   | (v, lineqs) :: hist' =>
   557     let val frees = map Free params
   565       let
   558         fun s_of_t t = Sign.string_of_term sg (subst_bounds (frees, t))
   566         val frees = map Free params
   559         val start = if v = ~1 then (hist', findex0 discr n lineqs)
   567         fun show_term t = ProofContext.string_of_term ctxt (subst_bounds (frees, t))
       
   568         val start =
       
   569           if v = ~1 then (hist', findex0 discr n lineqs)
   560           else (hist, replicate n Rat.zero)
   570           else (hist, replicate n Rat.zero)
   561         val ex = SOME (produce_ex ((map s_of_t atoms) ~~ discr)
   571         val ex = SOME (produce_ex (map show_term atoms ~~ discr)
   562           (uncurry (fold (findex1 discr)) start))
   572             (uncurry (fold (findex1 discr)) start))
   563           handle NoEx => NONE
   573           handle NoEx => NONE
   564     in
   574       in
   565       case ex of
   575         case ex of
   566         SOME s => (warning "arith failed - see trace for a counterexample"; tracing s)
   576           SOME s => (warning "arith failed - see trace for a counterexample"; tracing s)
   567       | NONE => warning "arith failed"
   577         | NONE => warning "arith failed"
   568     end;
   578       end;
   569 
   579 
   570 (* ------------------------------------------------------------------------- *)
   580 (* ------------------------------------------------------------------------- *)
   571 
   581 
   572 fun mknat (pTs : typ list) (ixs : int list) (atom : term, i : int) : lineq option =
   582 fun mknat (pTs : typ list) (ixs : int list) (atom : term, i : int) : lineq option =
   573   if LA_Logic.is_nat (pTs, atom)
   583   if LA_Logic.is_nat (pTs, atom)
   592 (*        could be intertwined: separate the first (fully split) case,       *)
   602 (*        could be intertwined: separate the first (fully split) case,       *)
   593 (*        refute it, continue with splitting and refuting.  Terminate with   *)
   603 (*        refute it, continue with splitting and refuting.  Terminate with   *)
   594 (*        failure as soon as a case could not be refuted; i.e. delay further *)
   604 (*        failure as soon as a case could not be refuted; i.e. delay further *)
   595 (*        splitting until after a refutation for other cases has been found. *)
   605 (*        splitting until after a refutation for other cases has been found. *)
   596 
   606 
   597 fun split_items sg (do_pre : bool) (Ts, terms) :
   607 fun split_items ctxt do_pre (Ts, terms) : (typ list * (LA_Data.decompT * int) list) list =
   598                 (typ list * (LA_Data.decompT * int) list) list =
       
   599 let
   608 let
   600   (* splits inequalities '~=' into '<' and '>'; this corresponds to *)
   609   (* splits inequalities '~=' into '<' and '>'; this corresponds to *)
   601   (* 'REPEAT_DETERM (eresolve_tac neqE i)' at the theorem/tactic    *)
   610   (* 'REPEAT_DETERM (eresolve_tac neqE i)' at the theorem/tactic    *)
   602   (* level                                                          *)
   611   (* level                                                          *)
   603   (* FIXME: this is currently sensitive to the order of theorems in *)
   612   (* FIXME: this is currently sensitive to the order of theorems in *)
   630     | number_hyps n (NONE::xs)     = number_hyps (n+1) xs
   639     | number_hyps n (NONE::xs)     = number_hyps (n+1) xs
   631     | number_hyps n ((SOME x)::xs) = (x, n) :: number_hyps (n+1) xs
   640     | number_hyps n ((SOME x)::xs) = (x, n) :: number_hyps (n+1) xs
   632 
   641 
   633   val result = (Ts, terms)
   642   val result = (Ts, terms)
   634     |> (* user-defined preprocessing of the subgoal *)
   643     |> (* user-defined preprocessing of the subgoal *)
   635        (if do_pre then LA_Data.pre_decomp sg else Library.single)
   644        (if do_pre then LA_Data.pre_decomp ctxt else Library.single)
   636     |> tap (fn subgoals => trace_msg ("Preprocessing yields " ^
   645     |> tap (fn subgoals => trace_msg ("Preprocessing yields " ^
   637          string_of_int (length subgoals) ^ " subgoal(s) total."))
   646          string_of_int (length subgoals) ^ " subgoal(s) total."))
   638     |> (* produce the internal encoding of (in-)equalities *)
   647     |> (* produce the internal encoding of (in-)equalities *)
   639        map (apsnd (map (fn t => (LA_Data.decomp sg t, LA_Data.domain_is_nat t))))
   648        map (apsnd (map (fn t => (LA_Data.decomp ctxt t, LA_Data.domain_is_nat t))))
   640     |> (* splitting of inequalities *)
   649     |> (* splitting of inequalities *)
   641        map (apsnd elim_neq)
   650        map (apsnd elim_neq)
   642     |> maps (fn (Ts, subgoals) => map (pair Ts o map fst) subgoals)
   651     |> maps (fn (Ts, subgoals) => map (pair Ts o map fst) subgoals)
   643     |> (* numbering of hypotheses, ignoring irrelevant ones *)
   652     |> (* numbering of hypotheses, ignoring irrelevant ones *)
   644        map (apsnd (number_hyps 0))
   653        map (apsnd (number_hyps 0))
   656   (map (pair d o fst) lhs) union ((map (pair d o fst) rhs) union dats);
   665   (map (pair d o fst) lhs) union ((map (pair d o fst) rhs) union dats);
   657 
   666 
   658 fun discr (initems : (LA_Data.decompT * int) list) : bool list =
   667 fun discr (initems : (LA_Data.decompT * int) list) : bool list =
   659   map fst (Library.foldl add_datoms ([],initems));
   668   map fst (Library.foldl add_datoms ([],initems));
   660 
   669 
   661 fun refutes (sg : theory) (params : (string * typ) list) (show_ex : bool) :
   670 fun refutes ctxt params show_ex :
   662   (typ list * (LA_Data.decompT * int) list) list -> injust list -> injust list option =
   671   (typ list * (LA_Data.decompT * int) list) list -> injust list -> injust list option =
   663 let
   672 let
   664   fun refute ((Ts : typ list, initems : (LA_Data.decompT * int) list)::initemss)
   673   fun refute ((Ts : typ list, initems : (LA_Data.decompT * int) list)::initemss)
   665              (js : injust list) : injust list option =
   674              (js : injust list) : injust list option =
   666     let val atoms = Library.foldl add_atoms ([], initems)
   675     let
   667         val n = length atoms
   676       val atoms = Library.foldl add_atoms ([], initems)
   668         val mkleq = mklineq n atoms
   677       val n = length atoms
   669         val ixs = 0 upto (n-1)
   678       val mkleq = mklineq n atoms
   670         val iatoms = atoms ~~ ixs
   679       val ixs = 0 upto (n - 1)
   671         val natlineqs = List.mapPartial (mknat Ts ixs) iatoms
   680       val iatoms = atoms ~~ ixs
   672         val ineqs = map mkleq initems @ natlineqs
   681       val natlineqs = List.mapPartial (mknat Ts ixs) iatoms
       
   682       val ineqs = map mkleq initems @ natlineqs
   673     in case elim (ineqs, []) of
   683     in case elim (ineqs, []) of
   674          Success j =>
   684          Success j =>
   675            (trace_msg ("Contradiction! (" ^ Int.toString (length js + 1) ^ ")");
   685            (trace_msg ("Contradiction! (" ^ string_of_int (length js + 1) ^ ")");
   676             refute initemss (js@[j]))
   686             refute initemss (js@[j]))
   677        | Failure hist =>
   687        | Failure hist =>
   678            (if not show_ex then
   688            (if not show_ex then
   679               ()
   689               ()
   680             else let
   690             else let
   683               (* snd params) as a suffix                                     *)
   693               (* snd params) as a suffix                                     *)
   684               val new_count = length Ts - length params - 1
   694               val new_count = length Ts - length params - 1
   685               val new_names = map Name.bound (0 upto new_count)
   695               val new_names = map Name.bound (0 upto new_count)
   686               val params'   = (new_names @ map fst params) ~~ Ts
   696               val params'   = (new_names @ map fst params) ~~ Ts
   687             in
   697             in
   688               trace_ex (sg, params', atoms, discr initems, n, hist)
   698               trace_ex ctxt params' atoms (discr initems) n hist
   689             end; NONE)
   699             end; NONE)
   690     end
   700     end
   691     | refute [] js = SOME js
   701     | refute [] js = SOME js
   692 in refute end;
   702 in refute end;
   693 
   703 
   694 fun refute (sg : theory) (params : (string * Term.typ) list) (show_ex : bool)
   704 fun refute ctxt params show_ex do_pre terms : injust list option =
   695            (do_pre : bool) (terms : term list) : injust list option =
   705   refutes ctxt params show_ex (split_items ctxt do_pre (map snd params, terms)) [];
   696   refutes sg params show_ex (split_items sg do_pre (map snd params, terms)) [];
       
   697 
   706 
   698 fun count P xs = length (filter P xs);
   707 fun count P xs = length (filter P xs);
   699 
   708 
   700 (* The limit on the number of ~= allowed.
   709 fun prove ctxt (params : (string * Term.typ) list) show_ex do_pre Hs concl : injust list option =
   701    Because each ~= is split into two cases, this can lead to an explosion.
       
   702 *)
       
   703 val fast_arith_neq_limit = ref 9;
       
   704 
       
   705 fun prove (sg : theory) (params : (string * Term.typ) list) (show_ex : bool)
       
   706           (do_pre : bool) (Hs : term list) (concl : term) : injust list option =
       
   707   let
   710   let
   708     val _ = trace_msg "prove:"
   711     val _ = trace_msg "prove:"
   709     (* append the negated conclusion to 'Hs' -- this corresponds to     *)
   712     (* append the negated conclusion to 'Hs' -- this corresponds to     *)
   710     (* 'DETERM (resolve_tac [LA_Logic.notI, LA_Logic.ccontr] i)' at the *)
   713     (* 'DETERM (resolve_tac [LA_Logic.notI, LA_Logic.ccontr] i)' at the *)
   711     (* theorem/tactic level                                             *)
   714     (* theorem/tactic level                                             *)
   712     val Hs' = Hs @ [LA_Logic.neg_prop concl]
   715     val Hs' = Hs @ [LA_Logic.neg_prop concl]
   713     fun is_neq NONE                 = false
   716     fun is_neq NONE                 = false
   714       | is_neq (SOME (_,_,r,_,_,_)) = (r = "~=")
   717       | is_neq (SOME (_,_,r,_,_,_)) = (r = "~=")
       
   718     val neq_limit = ConfigOption.get ctxt LA_Data.fast_arith_neq_limit;
   715   in
   719   in
   716     if count is_neq (map (LA_Data.decomp sg) Hs')
   720     if count is_neq (map (LA_Data.decomp ctxt) Hs') > neq_limit then
   717       > !fast_arith_neq_limit then (
   721      (trace_msg ("fast_arith_neq_limit exceeded (current value is " ^
   718       trace_msg ("fast_arith_neq_limit exceeded (current value is " ^
   722         string_of_int neq_limit ^ ")"); NONE)
   719                    string_of_int (!fast_arith_neq_limit) ^ ")");
   723     else
   720       NONE
   724       refute ctxt params show_ex do_pre Hs'
   721     ) else
       
   722       refute sg params show_ex do_pre Hs'
       
   723   end handle TERM ("neg_prop", _) =>
   725   end handle TERM ("neg_prop", _) =>
   724     (* since no meta-logic negation is available, we can only fail if   *)
   726     (* since no meta-logic negation is available, we can only fail if   *)
   725     (* the conclusion is not of the form 'Trueprop $ _' (simply         *)
   727     (* the conclusion is not of the form 'Trueprop $ _' (simply         *)
   726     (* dropping the conclusion doesn't work either, because even        *)
   728     (* dropping the conclusion doesn't work either, because even        *)
   727     (* 'False' does not imply arbitrary 'concl::prop')                  *)
   729     (* 'False' does not imply arbitrary 'concl::prop')                  *)
   728     (trace_msg "prove failed (cannot negate conclusion)."; NONE);
   730     (trace_msg "prove failed (cannot negate conclusion)."; NONE);
   729 
   731 
   730 fun refute_tac ss (i, justs) =
   732 fun refute_tac ss (i, justs) =
   731   fn state =>
   733   fn state =>
   732     let val _ = trace_thm ("refute_tac (on subgoal " ^ Int.toString i ^ ", with " ^
   734     let
   733                              Int.toString (length justs) ^ " justification(s)):") state
   735       val ctxt = Simplifier.the_context ss;
   734         val sg          = theory_of_thm state
   736       val _ = trace_thm ("refute_tac (on subgoal " ^ string_of_int i ^ ", with " ^
   735         val {neqE, ...} = Data.get sg
   737                              string_of_int (length justs) ^ " justification(s)):") state
   736         fun just1 j =
   738       val {neqE, ...} = get_data ctxt;
   737           (* eliminate inequalities *)
   739       fun just1 j =
   738           REPEAT_DETERM (eresolve_tac neqE i) THEN
   740         (* eliminate inequalities *)
   739             PRIMITIVE (trace_thm "State after neqE:") THEN
   741         REPEAT_DETERM (eresolve_tac neqE i) THEN
   740             (* use theorems generated from the actual justifications *)
   742           PRIMITIVE (trace_thm "State after neqE:") THEN
   741             METAHYPS (fn asms => rtac (mkthm (sg, ss) asms j) 1) i
   743           (* use theorems generated from the actual justifications *)
   742     in (* rewrite "[| A1; ...; An |] ==> B" to "[| A1; ...; An; ~B |] ==> False" *)
   744           METAHYPS (fn asms => rtac (mkthm ss asms j) 1) i
   743        DETERM (resolve_tac [LA_Logic.notI, LA_Logic.ccontr] i) THEN
   745     in
   744        (* user-defined preprocessing of the subgoal *)
   746       (* rewrite "[| A1; ...; An |] ==> B" to "[| A1; ...; An; ~B |] ==> False" *)
   745        DETERM (LA_Data.pre_tac i) THEN
   747       DETERM (resolve_tac [LA_Logic.notI, LA_Logic.ccontr] i) THEN
   746        PRIMITIVE (trace_thm "State after pre_tac:") THEN
   748       (* user-defined preprocessing of the subgoal *)
   747        (* prove every resulting subgoal, using its justification *)
   749       DETERM (LA_Data.pre_tac ctxt i) THEN
   748        EVERY (map just1 justs)
   750       PRIMITIVE (trace_thm "State after pre_tac:") THEN
       
   751       (* prove every resulting subgoal, using its justification *)
       
   752       EVERY (map just1 justs)
   749     end  state;
   753     end  state;
   750 
   754 
   751 (*
   755 (*
   752 Fast but very incomplete decider. Only premises and conclusions
   756 Fast but very incomplete decider. Only premises and conclusions
   753 that are already (negated) (in)equations are taken into account.
   757 that are already (negated) (in)equations are taken into account.
   754 *)
   758 *)
   755 fun simpset_lin_arith_tac (ss : simpset) (show_ex : bool) (i : int) (st : thm) =
   759 fun simpset_lin_arith_tac ss show_ex = SUBGOAL (fn (A, i) =>
   756   SUBGOAL (fn (A,_) =>
   760   let
   757   let val params = rev (Logic.strip_params A)
   761     val ctxt = Simplifier.the_context ss
   758       val Hs     = Logic.strip_assums_hyp A
   762     val params = rev (Logic.strip_params A)
   759       val concl  = Logic.strip_assums_concl A
   763     val Hs = Logic.strip_assums_hyp A
   760   in trace_thm ("Trying to refute subgoal " ^ string_of_int i) st;
   764     val concl = Logic.strip_assums_concl A
   761      case prove (Thm.theory_of_thm st) params show_ex true Hs concl of
   765     val _ = trace_term ctxt ("Trying to refute subgoal " ^ string_of_int i) A
   762        NONE => (trace_msg "Refutation failed."; no_tac)
   766   in
   763      | SOME js => (trace_msg "Refutation succeeded."; refute_tac ss (i, js))
   767     case prove ctxt params show_ex true Hs concl of
   764   end) i st;
   768       NONE => (trace_msg "Refutation failed."; no_tac)
   765 
   769     | SOME js => (trace_msg "Refutation succeeded."; refute_tac ss (i, js))
   766 fun lin_arith_tac (show_ex : bool) (i : int) (st : thm) =
   770   end);
   767   simpset_lin_arith_tac
   771 
   768     (Simplifier.theory_context (Thm.theory_of_thm st) Simplifier.empty_ss)
   772 fun cut_lin_arith_tac ss =
   769     show_ex i st;
   773   cut_facts_tac (Simplifier.prems_of_ss ss) THEN'
   770 
   774   simpset_lin_arith_tac ss false;
   771 fun cut_lin_arith_tac (ss : simpset) (i : int) =
   775 
   772   cut_facts_tac (Simplifier.prems_of_ss ss) i THEN
   776 fun lin_arith_tac ctxt =
   773   simpset_lin_arith_tac ss false i;
   777   simpset_lin_arith_tac (Simplifier.context ctxt Simplifier.empty_ss);
       
   778 
       
   779 
   774 
   780 
   775 (** Forward proof from theorems **)
   781 (** Forward proof from theorems **)
   776 
   782 
   777 (* More tricky code. Needs to arrange the proofs of the multiple cases (due
   783 (* More tricky code. Needs to arrange the proofs of the multiple cases (due
   778 to splits of ~= premises) such that it coincides with the order of the cases
   784 to splits of ~= premises) such that it coincides with the order of the cases
   792     val (_, Ict2r) = Thm.dest_comb Ir
   798     val (_, Ict2r) = Thm.dest_comb Ir
   793     val (Ict2, _)  = Thm.dest_comb Ict2r
   799     val (Ict2, _)  = Thm.dest_comb Ict2r
   794     val (_, ct2)   = Thm.dest_comb Ict2
   800     val (_, ct2)   = Thm.dest_comb Ict2
   795 in (ct1, ct2) end;
   801 in (ct1, ct2) end;
   796 
   802 
   797 fun splitasms (sg : theory) (asms : thm list) : splittree =
   803 fun splitasms ctxt (asms : thm list) : splittree =
   798 let val {neqE, ...} = Data.get sg
   804 let val {neqE, ...} = get_data ctxt
   799     fun elim_neq (asms', []) = Tip (rev asms')
   805     fun elim_neq (asms', []) = Tip (rev asms')
   800       | elim_neq (asms', asm::asms) =
   806       | elim_neq (asms', asm::asms) =
   801       (case get_first (fn th => SOME (asm COMP th) handle THM _ => NONE) neqE of
   807       (case get_first (fn th => SOME (asm COMP th) handle THM _ => NONE) neqE of
   802         SOME spl =>
   808         SOME spl =>
   803           let val (ct1, ct2) = extract (cprop_of spl)
   809           let val (ct1, ct2) = extract (cprop_of spl)
   806           in Spl (spl, ct1, elim_neq (asms', asms@[thm1]), ct2, elim_neq (asms', asms@[thm2]))
   812           in Spl (spl, ct1, elim_neq (asms', asms@[thm1]), ct2, elim_neq (asms', asms@[thm2]))
   807           end
   813           end
   808       | NONE => elim_neq (asm::asms', asms))
   814       | NONE => elim_neq (asm::asms', asms))
   809 in elim_neq ([], asms) end;
   815 in elim_neq ([], asms) end;
   810 
   816 
   811 fun fwdproof (ctxt : theory * simpset) (Tip asms : splittree) (j::js : injust list) =
   817 fun fwdproof ss (Tip asms : splittree) (j::js : injust list) = (mkthm ss asms j, js)
   812     (mkthm ctxt asms j, js)
   818   | fwdproof ss (Spl (thm, ct1, tree1, ct2, tree2)) js =
   813   | fwdproof ctxt (Spl (thm, ct1, tree1, ct2, tree2)) js =
   819       let
   814     let val (thm1, js1) = fwdproof ctxt tree1 js
   820         val (thm1, js1) = fwdproof ss tree1 js
   815         val (thm2, js2) = fwdproof ctxt tree2 js1
   821         val (thm2, js2) = fwdproof ss tree2 js1
   816         val thm1' = implies_intr ct1 thm1
   822         val thm1' = implies_intr ct1 thm1
   817         val thm2' = implies_intr ct2 thm2
   823         val thm2' = implies_intr ct2 thm2
   818     in (thm2' COMP (thm1' COMP thm), js2) end;
   824       in (thm2' COMP (thm1' COMP thm), js2) end;
   819 (* needs handle THM _ => NONE ? *)
   825       (* FIXME needs handle THM _ => NONE ? *)
   820 
   826 
   821 fun prover (ctxt as (sg, ss)) thms (Tconcl : term) (js : injust list) (pos : bool) : thm option =
   827 fun prover ss thms Tconcl (js : injust list) pos : thm option =
   822 let
   828   let
   823 (* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv *)
   829     val ctxt = Simplifier.the_context ss
   824 (* Use this code instead if lin_arith_prover calls prove with do_pre set to true *)
   830     val thy = ProofContext.theory_of ctxt
   825 (* but beware: this can be a significant performance issue.                      *)
   831     val nTconcl = LA_Logic.neg_prop Tconcl
   826     (* There is no "forward version" of 'pre_tac'.  Therefore we combine the     *)
   832     val cnTconcl = cterm_of thy nTconcl
   827     (* available theorems into a single proof state and perform "backward proof" *)
   833     val nTconclthm = assume cnTconcl
   828     (* using 'refute_tac'.                                                       *)
   834     val tree = splitasms ctxt (thms @ [nTconclthm])
   829 (*
   835     val (Falsethm, _) = fwdproof ss tree js
   830     val Hs    = map prop_of thms
   836     val contr = if pos then LA_Logic.ccontr else LA_Logic.notI
   831     val Prop  = fold (curry Logic.mk_implies) (rev Hs) Tconcl
   837     val concl = implies_intr cnTconcl Falsethm COMP contr
   832     val cProp = cterm_of sg Prop
   838   in SOME (trace_thm "Proved by lin. arith. prover:" (LA_Logic.mk_Eq concl)) end
   833     val concl = Goal.init cProp
   839   (*in case concl contains ?-var, which makes assume fail:*)   (* FIXME Variable.import_terms *)
   834                   |> refute_tac ss (1, js)
   840   handle THM _ => NONE;
   835                   |> Seq.hd
       
   836                   |> Goal.finish
       
   837                   |> fold (fn thA => fn thAB => implies_elim thAB thA) thms
       
   838 *)
       
   839 (* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *)
       
   840     val nTconcl       = LA_Logic.neg_prop Tconcl
       
   841     val cnTconcl      = cterm_of sg nTconcl
       
   842     val nTconclthm    = assume cnTconcl
       
   843     val tree          = splitasms sg (thms @ [nTconclthm])
       
   844     val (Falsethm, _) = fwdproof ctxt tree js
       
   845     val contr         = if pos then LA_Logic.ccontr else LA_Logic.notI
       
   846     val concl         = implies_intr cnTconcl Falsethm COMP contr
       
   847 in SOME (trace_thm "Proved by lin. arith. prover:"
       
   848           (LA_Logic.mk_Eq concl)) end
       
   849 (* in case concl contains ?-var, which makes assume fail: *)
       
   850 handle THM _ => NONE;
       
   851 
   841 
   852 (* PRE: concl is not negated!
   842 (* PRE: concl is not negated!
   853    This assumption is OK because
   843    This assumption is OK because
   854    1. lin_arith_prover tries both to prove and disprove concl and
   844    1. lin_arith_simproc tries both to prove and disprove concl and
   855    2. lin_arith_prover is applied by the simplifier which
   845    2. lin_arith_simproc is applied by the Simplifier which
   856       dives into terms and will thus try the non-negated concl anyway.
   846       dives into terms and will thus try the non-negated concl anyway.
   857 *)
   847 *)
   858 
   848 fun lin_arith_simproc ss concl =
   859 fun lin_arith_prover sg ss (concl : term) : thm option =
   849   let
   860 let val thms = List.concat (map LA_Logic.atomize (prems_of_ss ss));
   850     val ctxt = Simplifier.the_context ss
   861     val Hs = map prop_of thms
   851     val thms = List.concat (map LA_Logic.atomize (Simplifier.prems_of_ss ss))
       
   852     val Hs = map Thm.prop_of thms
   862     val Tconcl = LA_Logic.mk_Trueprop concl
   853     val Tconcl = LA_Logic.mk_Trueprop concl
   863 (*
   854   in
   864     val _ = trace_msg "lin_arith_prover"
   855     case prove ctxt [] false false Hs Tconcl of (* concl provable? *)
   865     val _ = map (trace_thm "thms:") thms
   856       SOME js => prover ss thms Tconcl js true
   866     val _ = trace_msg ("concl:" ^ Sign.string_of_term sg concl)
   857     | NONE =>
   867 *)
   858         let val nTconcl = LA_Logic.neg_prop Tconcl in
   868 in case prove sg [] false false Hs Tconcl of (* concl provable? *)
   859           case prove ctxt [] false false Hs nTconcl of (* ~concl provable? *)
   869      SOME js => prover (sg, ss) thms Tconcl js true
   860             SOME js => prover ss thms nTconcl js false
   870    | NONE => let val nTconcl = LA_Logic.neg_prop Tconcl
   861           | NONE => NONE
   871           in case prove sg [] false false Hs nTconcl of (* ~concl provable? *)
   862         end
   872                SOME js => prover (sg, ss) thms nTconcl js false
   863   end;
   873              | NONE => NONE
   864 
   874           end
       
   875 end;
   865 end;
   876 
       
   877 end;