src/Provers/Arith/fast_lin_arith.ML
changeset 13186 ef8ed6adcb38
parent 13105 3d1e7a199bdc
child 13464 c98321b8d638
equal deleted inserted replaced
13185:da61bfa0a391 13186:ef8ed6adcb38
   240 
   240 
   241 fun elim ineqs =
   241 fun elim ineqs =
   242   let val dummy = print_ineqs ineqs;
   242   let val dummy = print_ineqs ineqs;
   243       val (triv,nontriv) = partition is_trivial ineqs in
   243       val (triv,nontriv) = partition is_trivial ineqs in
   244   if not(null triv)
   244   if not(null triv)
   245   then case find_first is_answer triv of
   245   then case Library.find_first is_answer triv of
   246          None => elim nontriv | some => some
   246          None => elim nontriv | some => some
   247   else
   247   else
   248   if null nontriv then None else
   248   if null nontriv then None else
   249   let val (eqs,noneqs) = partition (fn (Lineq(_,ty,_,_)) => ty=Eq) nontriv in
   249   let val (eqs,noneqs) = partition (fn (Lineq(_,ty,_,_)) => ty=Eq) nontriv in
   250   if not(null eqs) then
   250   if not(null eqs) then
   345         | mk(Multiplied2(n,j)) = simp (trace_msg("**"^string_of_int n); trace_thm "**" (multn2(n,mk j)))
   345         | mk(Multiplied2(n,j)) = simp (trace_msg("**"^string_of_int n); trace_thm "**" (multn2(n,mk j)))
   346 
   346 
   347   in trace_msg "mkthm";
   347   in trace_msg "mkthm";
   348      let val thm = trace_thm "Final thm:" (mk just)
   348      let val thm = trace_thm "Final thm:" (mk just)
   349      in let val fls = simplify simpset thm
   349      in let val fls = simplify simpset thm
   350         in trace_thm "After simplification:" fls; fls
   350         in trace_thm "After simplification:" fls;
       
   351            if LA_Logic.is_False fls then fls
       
   352            else
       
   353             (tracing "Assumptions:"; seq print_thm asms;
       
   354              tracing "Proved:"; print_thm fls;
       
   355              warning "Linear arithmetic should have refuted the assumptions.\n\
       
   356                      \Please inform Tobias Nipkow (nipkow@in.tum.de).";
       
   357              fls)
   351         end
   358         end
   352      end handle FalseE thm => (trace_thm "False reached early:" thm; thm)
   359      end handle FalseE thm => (trace_thm "False reached early:" thm; thm)
   353   end
   360   end
   354 end;
   361 end;
   355 
   362 
   370         val lhsa = map (coeff lhs) atoms
   377         val lhsa = map (coeff lhs) atoms
   371         and rhsa = map (coeff rhs) atoms
   378         and rhsa = map (coeff rhs) atoms
   372         val diff = map2 (op -) (rhsa,lhsa)
   379         val diff = map2 (op -) (rhsa,lhsa)
   373         val c = i-j
   380         val c = i-j
   374         val just = Asm k
   381         val just = Asm k
   375         fun lineq(c,le,cs,j) = Some(Lineq(c,le,cs, if m=1 then j else Multiplied2(m,j)))
   382         fun lineq(c,le,cs,j) = Lineq(c,le,cs, if m=1 then j else Multiplied2(m,j))
   376     in case rel of
   383     in case rel of
   377         "<="   => lineq(c,Le,diff,just)
   384         "<="   => lineq(c,Le,diff,just)
   378        | "~<=" => if discrete
   385        | "~<=" => if discrete
   379                   then lineq(1-c,Le,map (op ~) diff,NotLeDD(just))
   386                   then lineq(1-c,Le,map (op ~) diff,NotLeDD(just))
   380                   else lineq(~c,Lt,map (op ~) diff,NotLeD(just))
   387                   else lineq(~c,Lt,map (op ~) diff,NotLeD(just))
   381        | "<"   => if discrete
   388        | "<"   => if discrete
   382                   then lineq(c+1,Le,diff,LessD(just))
   389                   then lineq(c+1,Le,diff,LessD(just))
   383                   else lineq(c,Lt,diff,just)
   390                   else lineq(c,Lt,diff,just)
   384        | "~<"  => lineq(~c,Le,map (op~) diff,NotLessD(just))
   391        | "~<"  => lineq(~c,Le,map (op~) diff,NotLessD(just))
   385        | "="   => lineq(c,Eq,diff,just)
   392        | "="   => lineq(c,Eq,diff,just)
   386        | "~="  => None
       
   387        | _     => sys_error("mklineq" ^ rel)   
   393        | _     => sys_error("mklineq" ^ rel)   
   388     end
   394     end
   389   end;
   395   end;
   390 
   396 
   391 fun mknat pTs ixs (atom,i) =
   397 fun mknat pTs ixs (atom,i) =
   392   if LA_Logic.is_nat(pTs,atom)
   398   if LA_Logic.is_nat(pTs,atom)
   393   then let val l = map (fn j => if j=i then 1 else 0) ixs
   399   then let val l = map (fn j => if j=i then 1 else 0) ixs
   394        in Some(Lineq(0,Le,l,Nat(i))) end
   400        in Some(Lineq(0,Le,l,Nat(i))) end
   395   else None
   401   else None
   396 
   402 
   397 fun abstract pTs items =
   403 (* This code is tricky. It takes a list of premises in the order they occur
   398   let val atoms = foldl (fn (ats,((lhs,_,_,rhs,_,_),_)) =>
   404 in the subgoal. Numerical premises are coded as Some(tuple), non-numerical
   399                             (map fst lhs) union ((map fst rhs) union ats))
   405 ones as None. Going through the premises, each numeric one is converted into
   400                         ([],items)
   406 a Lineq. The tricky bit is to convert ~= which is split into two cases < and
       
   407 >. Thus mklineqss returns a list of equation systems. This may blow up if
       
   408 there are many ~=, but in practice it does not seem to happen. The really
       
   409 tricky bit is to arrange the order of the cases such that they coincide with
       
   410 the order in which the cases are in the end generated by the tactic that
       
   411 applies the generated refutation thms (see function 'refute_tac').
       
   412 
       
   413 For variables n of type nat, a constraint 0 <= n is added.
       
   414 *)
       
   415 fun mklineqss(pTs,items) =
       
   416   let fun add(ats,None) = ats
       
   417         | add(ats,Some(lhs,_,_,rhs,_,_)) =
       
   418              (map fst lhs) union ((map fst rhs) union ats)
       
   419       val atoms = foldl add ([],items)
       
   420       val mkleq = mklineq atoms
   401       val ixs = 0 upto (length(atoms)-1)
   421       val ixs = 0 upto (length(atoms)-1)
   402       val iatoms = atoms ~~ ixs
   422       val iatoms = atoms ~~ ixs
   403   in mapfilter (mklineq atoms) items @ mapfilter (mknat pTs ixs) iatoms end;
   423       val natlineqs = mapfilter (mknat pTs ixs) iatoms
   404 
   424  
   405 (* Ordinary refutation *)
   425       fun elim_neq front _ [] = [front]
   406 fun refute1(pTs,items) =
   426         | elim_neq front n (None::ineqs) = elim_neq front (n+1) ineqs
   407   (case elim (abstract pTs items) of
   427         | elim_neq front n (Some(ineq as (l,i,rel,r,j,d))::ineqs) =
   408        None => []
   428           if rel = "~=" then elim_neq front n (ineqs @ [Some(l,i,"<",r,j,d)]) @
   409      | Some(Lineq(_,_,_,j)) => [j]);
   429                              elim_neq front n (ineqs @ [Some(r,j,"<",l,i,d)])
   410 
   430           else elim_neq (mkleq(ineq,n) :: front) (n+1) ineqs
   411 fun refute1_tac(i,just) =
   431 
       
   432   in elim_neq natlineqs 0 items end;
       
   433 
       
   434 fun elim_all (ineqs::ineqss) js =
       
   435   (case elim ineqs of None => None
       
   436    | Some(Lineq(_,_,_,j)) => elim_all ineqss (js@[j]))
       
   437   | elim_all [] js = Some js
       
   438 
       
   439 fun refute(pTsitems) = elim_all (mklineqss pTsitems) [];
       
   440 
       
   441 fun refute_tac(i,justs) =
   412   fn state =>
   442   fn state =>
   413     let val sg = #sign(rep_thm state)
   443     let val sg = #sign(rep_thm state)
   414     in resolve_tac [LA_Logic.notI,LA_Logic.ccontr] i THEN
   444         fun just1 j = REPEAT_DETERM(etac LA_Logic.neqE i) THEN
   415        METAHYPS (fn asms => rtac (mkthm sg asms just) 1) i
   445                       METAHYPS (fn asms => rtac (mkthm sg asms j) 1) i
       
   446     in DETERM(resolve_tac [LA_Logic.notI,LA_Logic.ccontr] i) THEN
       
   447        EVERY(map just1 justs)
   416     end
   448     end
   417     state;
   449     state;
   418 
   450 
   419 (* Double refutation caused by equality in conclusion *)
       
   420 fun refute2(pTs,items, (rhs,i,_,lhs,j,d), nHs) =
       
   421   (case elim (abstract pTs (items@[((rhs,i,"<",lhs,j,d),nHs)])) of
       
   422     None => []
       
   423   | Some(Lineq(_,_,_,j1)) =>
       
   424       (case elim (abstract pTs (items@[((lhs,j,"<",rhs,i,d),nHs)])) of
       
   425         None => []
       
   426       | Some(Lineq(_,_,_,j2)) => [j1,j2]));
       
   427 
       
   428 fun refute2_tac(i,just1,just2) =
       
   429   fn state => 
       
   430     let val sg = #sign(rep_thm state)
       
   431     in rtac LA_Logic.ccontr i THEN rotate_tac ~1 i THEN etac LA_Logic.neqE i THEN
       
   432        METAHYPS (fn asms => rtac (mkthm sg asms just1) 1) i THEN
       
   433        METAHYPS (fn asms => rtac (mkthm sg asms just2) 1) i
       
   434     end
       
   435     state;
       
   436 
       
   437 fun prove sg (pTs,Hs,concl) =
   451 fun prove sg (pTs,Hs,concl) =
   438 let val nHs = length Hs
   452 let val Hitems = map (LA_Data.decomp sg) Hs
   439     val ixHs = Hs ~~ (0 upto (nHs-1))
       
   440     val Hitems = mapfilter (fn (h,i) => case LA_Data.decomp sg h of
       
   441                                  None => None | Some(it) => Some(it,i)) ixHs
       
   442 in case LA_Data.decomp sg concl of
   453 in case LA_Data.decomp sg concl of
   443      None => if null Hitems then [] else refute1(pTs,Hitems)
   454      None => refute(pTs,Hitems@[None])
   444    | Some(citem as (r,i,rel,l,j,d)) =>
   455    | Some(citem as (r,i,rel,l,j,d)) =>
   445        if rel = "="
   456        let val neg::rel0 = explode rel
   446        then refute2(pTs,Hitems,citem,nHs)
   457            val nrel = if neg = "~" then implode rel0 else "~"^rel
   447        else let val neg::rel0 = explode rel
   458        in refute(pTs, Hitems @ [Some(r,i,nrel,l,j,d)]) end
   448                 val nrel = if neg = "~" then implode rel0 else "~"^rel
       
   449             in refute1(pTs, Hitems@[((r,i,nrel,l,j,d),nHs)]) end
       
   450 end;
   459 end;
   451 
   460 
   452 (*
   461 (*
   453 Fast but very incomplete decider. Only premises and conclusions
   462 Fast but very incomplete decider. Only premises and conclusions
   454 that are already (negated) (in)equations are taken into account.
   463 that are already (negated) (in)equations are taken into account.
   455 *)
   464 *)
   456 fun lin_arith_tac i st = SUBGOAL (fn (A,n) =>
   465 fun lin_arith_tac i st = SUBGOAL (fn (A,_) =>
   457   let val pTs = rev(map snd (Logic.strip_params A))
   466   let val pTs = rev(map snd (Logic.strip_params A))
   458       val Hs = Logic.strip_assums_hyp A
   467       val Hs = Logic.strip_assums_hyp A
   459       val concl = Logic.strip_assums_concl A
   468       val concl = Logic.strip_assums_concl A
   460   in trace_thm ("Trying to refute subgoal " ^ string_of_int i) st;
   469   in trace_thm ("Trying to refute subgoal " ^ string_of_int i) st;
   461      case prove (Thm.sign_of_thm st) (pTs,Hs,concl) of
   470      case prove (Thm.sign_of_thm st) (pTs,Hs,concl) of
   462        [j] => refute1_tac(n,j)
   471        None => no_tac
   463      | [j1,j2] => refute2_tac(n,j1,j2)
   472      | Some js => refute_tac(i,js)
   464      | _ => no_tac
       
   465   end) i st;
   473   end) i st;
   466 
   474 
   467 fun cut_lin_arith_tac thms i = cut_facts_tac thms i THEN lin_arith_tac i;
   475 fun cut_lin_arith_tac thms i = cut_facts_tac thms i THEN lin_arith_tac i;
   468 
   476 
   469 fun prover1(just,sg,thms,concl,pos) =
   477 (** Forward proof from theorems **)
   470 let val nconcl = LA_Logic.neg_prop concl
   478 
   471     val cnconcl = cterm_of sg nconcl
   479 (* More tricky code. Needs to arrange the proofs of the multiple cases (due
   472     val Fthm = mkthm sg (thms @ [assume cnconcl]) just
   480 to splits of ~= premises) such that it coincides with the order of the cases
       
   481 generated by function mklineqss. *)
       
   482 
       
   483 datatype splittree = Tip of thm list
       
   484                    | Spl of thm * cterm * splittree * cterm * splittree
       
   485 
       
   486 fun extract imp =
       
   487 let val (Il,r) = Thm.dest_comb imp
       
   488     val (_,imp1) = Thm.dest_comb Il
       
   489     val (Ict1,_) = Thm.dest_comb imp1
       
   490     val (_,ct1) = Thm.dest_comb Ict1
       
   491     val (Ir,_) = Thm.dest_comb r
       
   492     val (_,Ict2r) = Thm.dest_comb Ir
       
   493     val (Ict2,_) = Thm.dest_comb Ict2r
       
   494     val (_,ct2) = Thm.dest_comb Ict2
       
   495 in (ct1,ct2) end;
       
   496 
       
   497 fun splitasms asms =
       
   498 let fun split(asms',[]) = Tip(rev asms')
       
   499       | split(asms',asm::asms) =
       
   500       let val spl = asm COMP LA_Logic.neqE
       
   501           val (ct1,ct2) = extract(cprop_of spl)
       
   502           val thm1 = assume ct1 and thm2 = assume ct2
       
   503       in Spl(spl,ct1,split(asms',asms@[thm1]),ct2,split(asms',asms@[thm2])) end
       
   504       handle THM _ => split(asm::asms', asms)
       
   505 in split([],asms) end;
       
   506 
       
   507 fun fwdproof sg (Tip asms) (j::js) = (mkthm sg asms j, js)
       
   508   | fwdproof sg (Spl(thm,ct1,tree1,ct2,tree2)) js =
       
   509     let val (thm1,js1) = fwdproof sg tree1 js
       
   510         val (thm2,js2) = fwdproof sg tree2 js1
       
   511         val thm1' = implies_intr ct1 thm1
       
   512         val thm2' = implies_intr ct2 thm2
       
   513     in (thm2' COMP (thm1' COMP thm), js2) end;
       
   514 (* needs handle _ => None ? *)
       
   515 
       
   516 fun prover sg thms Tconcl js pos =
       
   517 let val nTconcl = LA_Logic.neg_prop Tconcl
       
   518     val cnTconcl = cterm_of sg nTconcl
       
   519     val nTconclthm = assume cnTconcl
       
   520     val tree = splitasms (thms @ [nTconclthm])
       
   521     val (thm,_) = fwdproof sg tree js
   473     val contr = if pos then LA_Logic.ccontr else LA_Logic.notI
   522     val contr = if pos then LA_Logic.ccontr else LA_Logic.notI
   474 in Some(LA_Logic.mk_Eq ((implies_intr cnconcl Fthm) COMP contr)) end
   523 in Some(LA_Logic.mk_Eq((implies_intr cnTconcl thm) COMP contr)) end
   475 handle _ => None;
   524 (* in case concl contains ?-var, which makes assume fail: *)
   476 
   525 handle THM _ => None;
   477 (* handle thm with equality conclusion *)
   526 
   478 fun prover2(just1,just2,sg,thms,concl) =
   527 (* PRE: concl is not negated!
   479 let val nconcl = LA_Logic.neg_prop concl (* m ~= n *)
   528    This assumption is OK because
   480     val cnconcl = cterm_of sg nconcl
   529    1. lin_arith_prover tries both to prove and disprove concl and
   481     val neqthm = assume cnconcl
   530    2. lin_arith_prover is applied by the simplifier which
   482     val casethm = neqthm COMP LA_Logic.neqE (* [|m<n ==> R; n<m ==> R|] ==> R *)
   531       dives into terms and will thus try the non-negated concl anyway.
   483     val [lessimp1,lessimp2] = prems_of casethm
   532 *)
   484     val less1 = fst(Logic.dest_implies lessimp1) (* m<n *)
       
   485     and less2 = fst(Logic.dest_implies lessimp2) (* n<m *)
       
   486     val cless1 = cterm_of sg less1 and cless2 = cterm_of sg less2
       
   487     val thm1 = mkthm sg (thms @ [assume cless1]) just1
       
   488     and thm2 = mkthm sg (thms @ [assume cless2]) just2
       
   489     val dthm1 = implies_intr cless1 thm1 and dthm2 = implies_intr cless2 thm2
       
   490     val thm = dthm2 COMP (dthm1 COMP casethm)
       
   491 in Some(LA_Logic.mk_Eq ((implies_intr cnconcl thm) COMP LA_Logic.ccontr)) end
       
   492 handle _ => None;
       
   493 
       
   494 (* PRE: concl is not negated! *)
       
   495 fun lin_arith_prover sg thms concl =
   533 fun lin_arith_prover sg thms concl =
   496 let val Hs = map (#prop o rep_thm) thms
   534 let val Hs = map (#prop o rep_thm) thms
   497     val Tconcl = LA_Logic.mk_Trueprop concl
   535     val Tconcl = LA_Logic.mk_Trueprop concl
   498 in case prove sg ([],Hs,Tconcl) of
   536 in case prove sg ([],Hs,Tconcl) of (* concl provable? *)
   499      [j] => prover1(j,sg,thms,Tconcl,true)
   537      Some js => prover sg thms Tconcl js true
   500    | [j1,j2] => prover2(j1,j2,sg,thms,Tconcl)
   538    | None => let val nTconcl = LA_Logic.neg_prop Tconcl
   501    | _ => let val nTconcl = LA_Logic.neg_prop Tconcl
   539           in case prove sg ([],Hs,nTconcl) of (* ~concl provable? *)
   502           in case prove sg ([],Hs,nTconcl) of
   540                Some js => prover sg thms nTconcl js false
   503                [j] => prover1(j,sg,thms,nTconcl,false)
   541              | None => None
   504                (* [_,_] impossible because of negation *)
       
   505              | _ => None
       
   506           end
   542           end
   507 end;
   543 end;
   508 
   544 
   509 end;
   545 end;