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; |