368 let val {discrete, inj_consts, ...} = ArithTheoryData.get sg |
369 let val {discrete, inj_consts, ...} = ArithTheoryData.get sg |
369 in decomp2 (sg,discrete,inj_consts) end |
370 in decomp2 (sg,discrete,inj_consts) end |
370 |
371 |
371 fun number_of(n,T) = HOLogic.number_of_const T $ (HOLogic.mk_binum n) |
372 fun number_of(n,T) = HOLogic.number_of_const T $ (HOLogic.mk_binum n) |
372 |
373 |
373 end; |
374 (*---------------------------------------------------------------------------*) |
|
375 (* code that performs certain goal transformations for linear arithmetic *) |
|
376 (*---------------------------------------------------------------------------*) |
|
377 |
|
378 (* A "do nothing" variant of pre_decomp and pre_tac: |
|
379 |
|
380 fun pre_decomp sg Ts termitems = [termitems]; |
|
381 fun pre_tac i = all_tac; |
|
382 *) |
|
383 |
|
384 (*---------------------------------------------------------------------------*) |
|
385 (* the following code performs splitting of certain constants (e.g. min, *) |
|
386 (* max) in a linear arithmetic problem; similar to what split_tac later does *) |
|
387 (* to the proof state *) |
|
388 (*---------------------------------------------------------------------------*) |
|
389 |
|
390 val fast_arith_split_limit = ref 9; |
|
391 |
|
392 (* checks whether splitting with 'thm' is implemented *) |
|
393 |
|
394 (* Thm.thm -> bool *) |
|
395 |
|
396 fun is_split_thm thm = |
|
397 case concl_of thm of _ $ (_ $ (_ $ lhs) $ _) => (* Trueprop $ ((op =) $ (?P $ lhs) $ rhs) *) |
|
398 (case head_of lhs of |
|
399 Const (a, _) => a mem_string ["Orderings.max", "Orderings.min", "HOL.abs", "HOL.minus", "IntDef.nat", "Divides.op mod", "Divides.op div"] |
|
400 | _ => (warning ("Lin. Arith.: wrong format for split rule " ^ Display.string_of_thm thm); false)) |
|
401 | _ => (warning ("Lin. Arith.: wrong format for split rule " ^ Display.string_of_thm thm); false); |
|
402 |
|
403 (* substitute new for occurrences of old in a term, incrementing bound *) |
|
404 (* variables as needed when substituting inside an abstraction *) |
|
405 |
|
406 (* (term * term) list -> term -> term *) |
|
407 |
|
408 fun subst_term [] t = t |
|
409 | subst_term pairs t = |
|
410 (case AList.lookup (op aconv) pairs t of |
|
411 SOME new => |
|
412 new |
|
413 | NONE => |
|
414 (case t of Abs (a, T, body) => |
|
415 let val pairs' = map (pairself (incr_boundvars 1)) pairs |
|
416 in Abs (a, T, subst_term pairs' body) end |
|
417 | t1 $ t2 => |
|
418 subst_term pairs t1 $ subst_term pairs t2 |
|
419 | _ => t)); |
|
420 |
|
421 (* approximates the effect of one application of split_tac (followed by NNF *) |
|
422 (* normalization) on the subgoal represented by '(Ts, terms)'; returns a *) |
|
423 (* list of new subgoals (each again represented by a typ list for bound *) |
|
424 (* variables and a term list for premises), or NONE if split_tac would fail *) |
|
425 (* on the subgoal *) |
|
426 |
|
427 (* theory -> typ list * term list -> (typ list * term list) list option *) |
|
428 |
|
429 (* FIXME: currently only the effect of certain split theorems is reproduced *) |
|
430 (* (which is why we need 'is_split_thm'). A more canonical *) |
|
431 (* implementation should analyze the right-hand side of the split *) |
|
432 (* theorem that can be applied, and modify the subgoal accordingly. *) |
|
433 |
|
434 fun split_once_items sg (Ts, terms) = |
|
435 let |
|
436 (* takes a list [t1, ..., tn] to the term *) |
|
437 (* tn' --> ... --> t1' --> False , *) |
|
438 (* where ti' = HOLogic.dest_Trueprop ti *) |
|
439 (* term list -> term *) |
|
440 fun REPEAT_DETERM_etac_rev_mp terms' = |
|
441 fold (curry HOLogic.mk_imp) (map HOLogic.dest_Trueprop terms') HOLogic.false_const |
|
442 val split_thms = filter is_split_thm (#splits (ArithTheoryData.get sg)) |
|
443 val cmap = Splitter.cmap_of_split_thms split_thms |
|
444 val splits = Splitter.split_posns cmap sg Ts (REPEAT_DETERM_etac_rev_mp terms) |
|
445 in |
|
446 if length splits > !fast_arith_split_limit then ( |
|
447 tracing ("fast_arith_split_limit exceeded (current value is " ^ string_of_int (!fast_arith_split_limit) ^ ")"); |
|
448 NONE |
|
449 ) else ( |
|
450 case splits of [] => |
|
451 NONE (* split_tac would fail: no possible split *) |
|
452 | ((_, _, _, split_type, split_term) :: _) => ( (* ignore all but the first possible split *) |
|
453 case strip_comb split_term of |
|
454 (* ?P (max ?i ?j) = ((?i <= ?j --> ?P ?j) & (~ ?i <= ?j --> ?P ?i)) *) |
|
455 (Const ("Orderings.max", _), [t1, t2]) => |
|
456 let |
|
457 val rev_terms = rev terms |
|
458 val terms1 = map (subst_term [(split_term, t1)]) rev_terms |
|
459 val terms2 = map (subst_term [(split_term, t2)]) rev_terms |
|
460 val t1_leq_t2 = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ t1 $ t2 |
|
461 val not_t1_leq_t2 = HOLogic.Not $ t1_leq_t2 |
|
462 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
463 val subgoal1 = (HOLogic.mk_Trueprop t1_leq_t2) :: terms2 @ [not_false] |
|
464 val subgoal2 = (HOLogic.mk_Trueprop not_t1_leq_t2) :: terms1 @ [not_false] |
|
465 in |
|
466 SOME [(Ts, subgoal1), (Ts, subgoal2)] |
|
467 end |
|
468 (* ?P (min ?i ?j) = ((?i <= ?j --> ?P ?i) & (~ ?i <= ?j --> ?P ?j)) *) |
|
469 | (Const ("Orderings.min", _), [t1, t2]) => |
|
470 let |
|
471 val rev_terms = rev terms |
|
472 val terms1 = map (subst_term [(split_term, t1)]) rev_terms |
|
473 val terms2 = map (subst_term [(split_term, t2)]) rev_terms |
|
474 val t1_leq_t2 = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ t1 $ t2 |
|
475 val not_t1_leq_t2 = HOLogic.Not $ t1_leq_t2 |
|
476 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
477 val subgoal1 = (HOLogic.mk_Trueprop t1_leq_t2) :: terms1 @ [not_false] |
|
478 val subgoal2 = (HOLogic.mk_Trueprop not_t1_leq_t2) :: terms2 @ [not_false] |
|
479 in |
|
480 SOME [(Ts, subgoal1), (Ts, subgoal2)] |
|
481 end |
|
482 (* ?P (abs ?a) = ((0 <= ?a --> ?P ?a) & (?a < 0 --> ?P (- ?a))) *) |
|
483 | (Const ("HOL.abs", _), [t1]) => |
|
484 let |
|
485 val rev_terms = rev terms |
|
486 val terms1 = map (subst_term [(split_term, t1)]) rev_terms |
|
487 val terms2 = map (subst_term [(split_term, Const ("HOL.uminus", split_type --> split_type) $ t1)]) rev_terms |
|
488 val zero = Const ("0", split_type) |
|
489 val zero_leq_t1 = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ zero $ t1 |
|
490 val t1_lt_zero = Const ("Orderings.less", split_type --> split_type --> HOLogic.boolT) $ t1 $ zero |
|
491 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
492 val subgoal1 = (HOLogic.mk_Trueprop zero_leq_t1) :: terms1 @ [not_false] |
|
493 val subgoal2 = (HOLogic.mk_Trueprop t1_lt_zero) :: terms2 @ [not_false] |
|
494 in |
|
495 SOME [(Ts, subgoal1), (Ts, subgoal2)] |
|
496 end |
|
497 (* ?P (?a - ?b) = ((?a < ?b --> ?P 0) & (ALL d. ?a = ?b + d --> ?P d)) *) |
|
498 | (Const ("HOL.minus", _), [t1, t2]) => |
|
499 let |
|
500 (* "d" in the above theorem becomes a new bound variable after NNF *) |
|
501 (* transformation, therefore some adjustment of indices is necessary *) |
|
502 val rev_terms = rev terms |
|
503 val zero = Const ("0", split_type) |
|
504 val d = Bound 0 |
|
505 val terms1 = map (subst_term [(split_term, zero)]) rev_terms |
|
506 val terms2 = map (subst_term [(incr_boundvars 1 split_term, d)]) (map (incr_boundvars 1) rev_terms) |
|
507 val t1' = incr_boundvars 1 t1 |
|
508 val t2' = incr_boundvars 1 t2 |
|
509 val t1_lt_t2 = Const ("Orderings.less", split_type --> split_type --> HOLogic.boolT) $ t1 $ t2 |
|
510 val t1_eq_t2_plus_d = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t1' $ (Const ("HOL.plus", split_type --> split_type --> split_type) $ t2' $ d) |
|
511 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
512 val subgoal1 = (HOLogic.mk_Trueprop t1_lt_t2) :: terms1 @ [not_false] |
|
513 val subgoal2 = (HOLogic.mk_Trueprop t1_eq_t2_plus_d) :: terms2 @ [not_false] |
|
514 in |
|
515 SOME [(Ts, subgoal1), (split_type :: Ts, subgoal2)] |
|
516 end |
|
517 (* ?P (nat ?i) = ((ALL n. ?i = int n --> ?P n) & (?i < 0 --> ?P 0)) *) |
|
518 | (Const ("IntDef.nat", _), [t1]) => |
|
519 let |
|
520 val rev_terms = rev terms |
|
521 val zero_int = Const ("0", HOLogic.intT) |
|
522 val zero_nat = Const ("0", HOLogic.natT) |
|
523 val n = Bound 0 |
|
524 val terms1 = map (subst_term [(incr_boundvars 1 split_term, n)]) (map (incr_boundvars 1) rev_terms) |
|
525 val terms2 = map (subst_term [(split_term, zero_nat)]) rev_terms |
|
526 val t1' = incr_boundvars 1 t1 |
|
527 val t1_eq_int_n = Const ("op =", HOLogic.intT --> HOLogic.intT --> HOLogic.boolT) $ t1' $ (Const ("IntDef.int", HOLogic.natT --> HOLogic.intT) $ n) |
|
528 val t1_lt_zero = Const ("Orderings.less", HOLogic.intT --> HOLogic.intT --> HOLogic.boolT) $ t1 $ zero_int |
|
529 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
530 val subgoal1 = (HOLogic.mk_Trueprop t1_eq_int_n) :: terms1 @ [not_false] |
|
531 val subgoal2 = (HOLogic.mk_Trueprop t1_lt_zero) :: terms2 @ [not_false] |
|
532 in |
|
533 SOME [(HOLogic.natT :: Ts, subgoal1), (Ts, subgoal2)] |
|
534 end |
|
535 (* "?P ((?n::nat) mod (number_of ?k)) = ((number_of ?k = 0 --> ?P ?n) & (~ (number_of ?k = 0) --> (ALL i j. j < number_of ?k --> ?n = number_of ?k * i + j --> ?P j))) *) |
|
536 | (Const ("Divides.op mod", Type ("fun", [Type ("nat", []), _])), [t1, t2]) => |
|
537 let |
|
538 val rev_terms = rev terms |
|
539 val zero = Const ("0", split_type) |
|
540 val i = Bound 1 |
|
541 val j = Bound 0 |
|
542 val terms1 = map (subst_term [(split_term, t1)]) rev_terms |
|
543 val terms2 = map (subst_term [(incr_boundvars 2 split_term, j)]) (map (incr_boundvars 2) rev_terms) |
|
544 val t1' = incr_boundvars 2 t1 |
|
545 val t2' = incr_boundvars 2 t2 |
|
546 val t2_eq_zero = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t2 $ zero |
|
547 val t2_neq_zero = HOLogic.mk_not (Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t2' $ zero) |
|
548 val j_lt_t2 = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ j $ t2' |
|
549 val t1_eq_t2_times_i_plus_j = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t1' $ |
|
550 (Const ("HOL.plus", split_type --> split_type --> split_type) $ |
|
551 (Const ("HOL.times", split_type --> split_type --> split_type) $ t2' $ i) $ j) |
|
552 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
553 val subgoal1 = (HOLogic.mk_Trueprop t2_eq_zero) :: terms1 @ [not_false] |
|
554 val subgoal2 = (map HOLogic.mk_Trueprop [t2_neq_zero, j_lt_t2, t1_eq_t2_times_i_plus_j]) @ terms2 @ [not_false] |
|
555 in |
|
556 SOME [(Ts, subgoal1), (split_type :: split_type :: Ts, subgoal2)] |
|
557 end |
|
558 (* "?P ((?n::nat) div (number_of ?k)) = ((number_of ?k = 0 --> ?P 0) & (~ (number_of ?k = 0) --> (ALL i j. j < number_of ?k --> ?n = number_of ?k * i + j --> ?P i))) *) |
|
559 | (Const ("Divides.op div", Type ("fun", [Type ("nat", []), _])), [t1, t2]) => |
|
560 let |
|
561 val rev_terms = rev terms |
|
562 val zero = Const ("0", split_type) |
|
563 val i = Bound 1 |
|
564 val j = Bound 0 |
|
565 val terms1 = map (subst_term [(split_term, zero)]) rev_terms |
|
566 val terms2 = map (subst_term [(incr_boundvars 2 split_term, i)]) (map (incr_boundvars 2) rev_terms) |
|
567 val t1' = incr_boundvars 2 t1 |
|
568 val t2' = incr_boundvars 2 t2 |
|
569 val t2_eq_zero = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t2 $ zero |
|
570 val t2_neq_zero = HOLogic.mk_not (Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t2' $ zero) |
|
571 val j_lt_t2 = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ j $ t2' |
|
572 val t1_eq_t2_times_i_plus_j = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t1' $ |
|
573 (Const ("HOL.plus", split_type --> split_type --> split_type) $ |
|
574 (Const ("HOL.times", split_type --> split_type --> split_type) $ t2' $ i) $ j) |
|
575 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
576 val subgoal1 = (HOLogic.mk_Trueprop t2_eq_zero) :: terms1 @ [not_false] |
|
577 val subgoal2 = (map HOLogic.mk_Trueprop [t2_neq_zero, j_lt_t2, t1_eq_t2_times_i_plus_j]) @ terms2 @ [not_false] |
|
578 in |
|
579 SOME [(Ts, subgoal1), (split_type :: split_type :: Ts, subgoal2)] |
|
580 end |
|
581 (* "?P ((?n::int) mod (number_of ?k)) = ((iszero (number_of ?k) --> ?P ?n) & |
|
582 (neg (number_of (bin_minus ?k)) --> (ALL i j. 0 <= j & j < number_of ?k & ?n = number_of ?k * i + j --> ?P j)) & |
|
583 (neg (number_of ?k) --> (ALL i j. number_of ?k < j & j <= 0 & ?n = number_of ?k * i + j --> ?P j))) *) |
|
584 | (Const ("Divides.op mod", Type ("fun", [Type ("IntDef.int", []), _])), [t1, t2 as (number_of $ k)]) => |
|
585 let |
|
586 val rev_terms = rev terms |
|
587 val zero = Const ("0", split_type) |
|
588 val i = Bound 1 |
|
589 val j = Bound 0 |
|
590 val terms1 = map (subst_term [(split_term, t1)]) rev_terms |
|
591 val terms2_3 = map (subst_term [(incr_boundvars 2 split_term, j)]) (map (incr_boundvars 2) rev_terms) |
|
592 val t1' = incr_boundvars 2 t1 |
|
593 val (t2' as (_ $ k')) = incr_boundvars 2 t2 |
|
594 val iszero_t2 = Const ("IntDef.iszero", split_type --> HOLogic.boolT) $ t2 |
|
595 val neg_minus_k = Const ("IntDef.neg", split_type --> HOLogic.boolT) $ |
|
596 (number_of $ (Const ("Numeral.bin_minus", HOLogic.binT --> HOLogic.binT) $ k')) |
|
597 val zero_leq_j = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ zero $ j |
|
598 val j_lt_t2 = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ j $ t2' |
|
599 val t1_eq_t2_times_i_plus_j = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t1' $ |
|
600 (Const ("HOL.plus", split_type --> split_type --> split_type) $ |
|
601 (Const ("HOL.times", split_type --> split_type --> split_type) $ t2' $ i) $ j) |
|
602 val neg_t2 = Const ("IntDef.neg", split_type --> HOLogic.boolT) $ t2' |
|
603 val t2_lt_j = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ t2' $ j |
|
604 val j_leq_zero = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ j $ zero |
|
605 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
606 val subgoal1 = (HOLogic.mk_Trueprop iszero_t2) :: terms1 @ [not_false] |
|
607 val subgoal2 = (map HOLogic.mk_Trueprop [neg_minus_k, zero_leq_j]) |
|
608 @ hd terms2_3 |
|
609 :: (if tl terms2_3 = [] then [not_false] else []) |
|
610 @ (map HOLogic.mk_Trueprop [j_lt_t2, t1_eq_t2_times_i_plus_j]) |
|
611 @ (if tl terms2_3 = [] then [] else tl terms2_3 @ [not_false]) |
|
612 val subgoal3 = (map HOLogic.mk_Trueprop [neg_t2, t2_lt_j]) |
|
613 @ hd terms2_3 |
|
614 :: (if tl terms2_3 = [] then [not_false] else []) |
|
615 @ (map HOLogic.mk_Trueprop [j_leq_zero, t1_eq_t2_times_i_plus_j]) |
|
616 @ (if tl terms2_3 = [] then [] else tl terms2_3 @ [not_false]) |
|
617 val Ts' = split_type :: split_type :: Ts |
|
618 in |
|
619 SOME [(Ts, subgoal1), (Ts', subgoal2), (Ts', subgoal3)] |
|
620 end |
|
621 (* "?P ((?n::int) div (number_of ?k)) = ((iszero (number_of ?k) --> ?P 0) & |
|
622 (neg (number_of (bin_minus ?k)) --> (ALL i. (EX j. 0 <= j & j < number_of ?k & ?n = number_of ?k * i + j) --> ?P i)) & |
|
623 (neg (number_of ?k) --> (ALL i. (EX j. number_of ?k < j & j <= 0 & ?n = number_of ?k * i + j) --> ?P i))) *) |
|
624 | (Const ("Divides.op div", Type ("fun", [Type ("IntDef.int", []), _])), [t1, t2 as (number_of $ k)]) => |
|
625 let |
|
626 val rev_terms = rev terms |
|
627 val zero = Const ("0", split_type) |
|
628 val i = Bound 1 |
|
629 val j = Bound 0 |
|
630 val terms1 = map (subst_term [(split_term, zero)]) rev_terms |
|
631 val terms2_3 = map (subst_term [(incr_boundvars 2 split_term, i)]) (map (incr_boundvars 2) rev_terms) |
|
632 val t1' = incr_boundvars 2 t1 |
|
633 val (t2' as (_ $ k')) = incr_boundvars 2 t2 |
|
634 val iszero_t2 = Const ("IntDef.iszero", split_type --> HOLogic.boolT) $ t2 |
|
635 val neg_minus_k = Const ("IntDef.neg", split_type --> HOLogic.boolT) $ |
|
636 (number_of $ (Const ("Numeral.bin_minus", HOLogic.binT --> HOLogic.binT) $ k')) |
|
637 val zero_leq_j = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ zero $ j |
|
638 val j_lt_t2 = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ j $ t2' |
|
639 val t1_eq_t2_times_i_plus_j = Const ("op =", split_type --> split_type --> HOLogic.boolT) $ t1' $ |
|
640 (Const ("HOL.plus", split_type --> split_type --> split_type) $ |
|
641 (Const ("HOL.times", split_type --> split_type --> split_type) $ t2' $ i) $ j) |
|
642 val neg_t2 = Const ("IntDef.neg", split_type --> HOLogic.boolT) $ t2' |
|
643 val t2_lt_j = Const ("Orderings.less", split_type --> split_type--> HOLogic.boolT) $ t2' $ j |
|
644 val j_leq_zero = Const ("Orderings.less_eq", split_type --> split_type --> HOLogic.boolT) $ j $ zero |
|
645 val not_false = HOLogic.mk_Trueprop (HOLogic.Not $ HOLogic.false_const) |
|
646 val subgoal1 = (HOLogic.mk_Trueprop iszero_t2) :: terms1 @ [not_false] |
|
647 val subgoal2 = (HOLogic.mk_Trueprop neg_minus_k) |
|
648 :: terms2_3 |
|
649 @ not_false |
|
650 :: (map HOLogic.mk_Trueprop [zero_leq_j, j_lt_t2, t1_eq_t2_times_i_plus_j]) |
|
651 val subgoal3 = (HOLogic.mk_Trueprop neg_t2) |
|
652 :: terms2_3 |
|
653 @ not_false |
|
654 :: (map HOLogic.mk_Trueprop [t2_lt_j, j_leq_zero, t1_eq_t2_times_i_plus_j]) |
|
655 val Ts' = split_type :: split_type :: Ts |
|
656 in |
|
657 SOME [(Ts, subgoal1), (Ts', subgoal2), (Ts', subgoal3)] |
|
658 end |
|
659 (* this will only happen if a split theorem can be applied for which no code exists above -- *) |
|
660 (* in which case either the split theorem should be implemented above, or 'is_split_thm' *) |
|
661 (* should be modified to filter it out *) |
|
662 | (t, ts) => ( |
|
663 warning ("Lin. Arith.: split rule for " ^ Sign.string_of_term sg t ^ " (with " ^ Int.toString (length ts) ^ |
|
664 " argument(s)) not implemented; proof reconstruction is likely to fail"); |
|
665 NONE |
|
666 )) |
|
667 ) |
|
668 end; |
|
669 |
|
670 (* remove terms that do not satisfy p; change the order of the remaining *) |
|
671 (* terms in the same way as filter_prems_tac does *) |
|
672 |
|
673 (* (term -> bool) -> term list -> term list *) |
|
674 |
|
675 fun filter_prems_tac_items p terms = |
|
676 let |
|
677 fun filter_prems (t, (left, right)) = |
|
678 if p t then (left, right @ [t]) else (left @ right, []) |
|
679 val (left, right) = foldl filter_prems ([], []) terms |
|
680 in |
|
681 right @ left |
|
682 end; |
|
683 |
|
684 (* return true iff TRY (etac notE) THEN eq_assume_tac would succeed on a *) |
|
685 (* subgoal that has 'terms' as premises *) |
|
686 |
|
687 (* term list -> bool *) |
|
688 |
|
689 fun negated_term_occurs_positively terms = |
|
690 List.exists (fn (TP $ (Const ("Not", _) $ t)) => member (op aconv) terms (TP $ t) | _ => false) terms; |
|
691 |
|
692 (* theory -> typ list * term list -> (typ list * term list) list *) |
|
693 |
|
694 fun pre_decomp sg (Ts, terms) = |
|
695 let |
|
696 (* repeatedly split (including newly emerging subgoals) until no further *) |
|
697 (* splitting is possible *) |
|
698 (* (typ list * term list) list -> (typ list * term list) list *) |
|
699 fun split_loop [] = [] |
|
700 | split_loop (subgoal::subgoals) = ( |
|
701 case split_once_items sg subgoal of |
|
702 SOME new_subgoals => split_loop (new_subgoals @ subgoals) |
|
703 | NONE => subgoal :: split_loop subgoals |
|
704 ) |
|
705 fun is_relevant t = isSome (decomp sg t) |
|
706 val relevant_terms = filter_prems_tac_items is_relevant terms (* filter_prems_tac is_relevant *) |
|
707 val split_goals = split_loop [(Ts, relevant_terms)] (* split_tac, NNF normalization *) |
|
708 val beta_eta_norm = map (apsnd (map (Envir.eta_contract o Envir.beta_norm))) split_goals (* necessary because split_once_tac may normalize terms *) |
|
709 val result = List.filter (not o negated_term_occurs_positively o snd) beta_eta_norm (* TRY (etac notE) THEN eq_assume_tac *) |
|
710 in |
|
711 result |
|
712 end; |
|
713 |
|
714 (* takes the i-th subgoal [| A1; ...; An |] ==> B to *) |
|
715 (* An --> ... --> A1 --> B, performs splitting with the given 'split_thms' *) |
|
716 (* (resulting in a different subgoal P), takes P to ~P ==> False, *) |
|
717 (* performs NNF-normalization of ~P, and eliminates conjunctions, *) |
|
718 (* disjunctions and existential quantifiers from the premises, possibly (in *) |
|
719 (* the case of disjunctions) resulting in several new subgoals, each of the *) |
|
720 (* general form [| Q1; ...; Qm |] ==> False. Fails if more than *) |
|
721 (* !fast_arith_split_limit splits are possible. *) |
|
722 |
|
723 (* Thm.thm list -> int -> Tactical.tactic *) |
|
724 |
|
725 fun split_once_tac split_thms i = |
|
726 let |
|
727 val nnf_simpset = |
|
728 empty_ss setmkeqTrue mk_eq_True |
|
729 setmksimps (mksimps mksimps_pairs) |
|
730 addsimps [imp_conv_disj, iff_conv_conj_imp, de_Morgan_disj, de_Morgan_conj, |
|
731 not_all, not_ex, not_not] |
|
732 fun prem_nnf_tac i st = |
|
733 full_simp_tac (Simplifier.theory_context (Thm.theory_of_thm st) nnf_simpset) i st |
|
734 fun cond_split_tac i st = |
|
735 let |
|
736 val subgoal = Logic.nth_prem (i, Thm.prop_of st) |
|
737 val Ts = rev (map snd (Logic.strip_params subgoal)) |
|
738 val concl = HOLogic.dest_Trueprop (Logic.strip_assums_concl subgoal) |
|
739 val cmap = Splitter.cmap_of_split_thms split_thms |
|
740 val splits = Splitter.split_posns cmap (theory_of_thm st) Ts concl |
|
741 in |
|
742 if length splits > !fast_arith_split_limit then |
|
743 no_tac st |
|
744 else |
|
745 split_tac split_thms i st |
|
746 end |
|
747 in |
|
748 EVERY' [ |
|
749 REPEAT_DETERM o etac rev_mp, |
|
750 cond_split_tac, |
|
751 rtac ccontr, |
|
752 prem_nnf_tac, |
|
753 TRY o REPEAT_ALL_NEW (DETERM o (eresolve_tac [conjE, exE] ORELSE' etac disjE)) |
|
754 ] i |
|
755 end; |
|
756 |
|
757 (* remove irrelevant premises, then split the i-th subgoal (and all new *) |
|
758 (* subgoals) by using 'split_once_tac' repeatedly. Beta-eta-normalize new *) |
|
759 (* subgoals and finally attempt to solve them by finding an immediate *) |
|
760 (* contradiction (i.e. a term and its negation) in their premises. *) |
|
761 |
|
762 (* int -> Tactical.tactic *) |
|
763 |
|
764 fun pre_tac i st = |
|
765 let |
|
766 val sg = theory_of_thm st |
|
767 val split_thms = filter is_split_thm (#splits (ArithTheoryData.get sg)) |
|
768 fun is_relevant t = isSome (decomp sg t) |
|
769 in |
|
770 DETERM ( |
|
771 TRY (filter_prems_tac is_relevant i) |
|
772 THEN ( |
|
773 (TRY o REPEAT_ALL_NEW (split_once_tac split_thms)) |
|
774 THEN_ALL_NEW |
|
775 ((fn j => PRIMITIVE (Drule.fconv_rule (Drule.goals_conv (equal j) (Drule.beta_eta_conversion)))) |
|
776 THEN' |
|
777 (TRY o (etac notE THEN' eq_assume_tac))) |
|
778 ) i |
|
779 ) st |
|
780 end; |
|
781 |
|
782 end; (* LA_Data_Ref *) |
374 |
783 |
375 |
784 |
376 structure Fast_Arith = |
785 structure Fast_Arith = |
377 Fast_Lin_Arith(structure LA_Logic=LA_Logic and LA_Data=LA_Data_Ref); |
786 Fast_Lin_Arith(structure LA_Logic=LA_Logic and LA_Data=LA_Data_Ref); |
378 |
787 |
379 val fast_arith_tac = Fast_Arith.lin_arith_tac false |
788 val fast_arith_tac = Fast_Arith.lin_arith_tac false; |
380 and fast_ex_arith_tac = Fast_Arith.lin_arith_tac |
789 val fast_ex_arith_tac = Fast_Arith.lin_arith_tac; |
381 and trace_arith = Fast_Arith.trace |
790 val trace_arith = Fast_Arith.trace; |
382 and fast_arith_neq_limit = Fast_Arith.fast_arith_neq_limit; |
791 val fast_arith_neq_limit = Fast_Arith.fast_arith_neq_limit; |
|
792 val fast_arith_split_limit = LA_Data_Ref.fast_arith_split_limit; |
383 |
793 |
384 local |
794 local |
385 |
795 |
386 (* reduce contradictory <= to False. |
796 (* reduce contradictory <= to False. |
387 Most of the work is done by the cancel tactics. |
797 Most of the work is done by the cancel tactics. |
437 |
847 |
438 val fast_nat_arith_simproc = |
848 val fast_nat_arith_simproc = |
439 Simplifier.simproc (the_context ()) "fast_nat_arith" |
849 Simplifier.simproc (the_context ()) "fast_nat_arith" |
440 ["(m::nat) < n","(m::nat) <= n", "(m::nat) = n"] Fast_Arith.lin_arith_prover; |
850 ["(m::nat) < n","(m::nat) <= n", "(m::nat) = n"] Fast_Arith.lin_arith_prover; |
441 |
851 |
442 |
|
443 (* Because of fast_nat_arith_simproc, the arithmetic solver is really only |
852 (* Because of fast_nat_arith_simproc, the arithmetic solver is really only |
444 useful to detect inconsistencies among the premises for subgoals which are |
853 useful to detect inconsistencies among the premises for subgoals which are |
445 *not* themselves (in)equalities, because the latter activate |
854 *not* themselves (in)equalities, because the latter activate |
446 fast_nat_arith_simproc anyway. However, it seems cheaper to activate the |
855 fast_nat_arith_simproc anyway. However, it seems cheaper to activate the |
447 solver all the time rather than add the additional check. *) |
856 solver all the time rather than add the additional check. *) |
448 |
857 |
449 |
858 |
450 (* arith proof method *) |
859 (* arith proof method *) |
451 |
860 |
452 (* FIXME: K true should be replaced by a sensible test to speed things up |
|
453 in case there are lots of irrelevant terms involved; |
|
454 elimination of min/max can be optimized: |
|
455 (max m n + k <= r) = (m+k <= r & n+k <= r) |
|
456 (l <= min m n + k) = (l <= m+k & l <= n+k) |
|
457 *) |
|
458 local |
861 local |
459 (* a simpset for computations subject to optimization !!! *) |
862 |
460 (* |
|
461 val binarith = map thm |
|
462 ["Pls_0_eq", "Min_1_eq", |
|
463 "bin_pred_Pls","bin_pred_Min","bin_pred_1","bin_pred_0", |
|
464 "bin_succ_Pls", "bin_succ_Min", "bin_succ_1", "bin_succ_0", |
|
465 "bin_add_Pls", "bin_add_Min", "bin_add_BIT_0", "bin_add_BIT_10", |
|
466 "bin_add_BIT_11", "bin_minus_Pls", "bin_minus_Min", "bin_minus_1", |
|
467 "bin_minus_0", "bin_mult_Pls", "bin_mult_Min", "bin_mult_1", "bin_mult_0", |
|
468 "bin_add_Pls_right", "bin_add_Min_right"]; |
|
469 val intarithrel = |
|
470 (map thm ["int_eq_number_of_eq","int_neg_number_of_BIT", |
|
471 "int_le_number_of_eq","int_iszero_number_of_0", |
|
472 "int_less_number_of_eq_neg"]) @ |
|
473 (map (fn s => thm s RS thm "lift_bool") |
|
474 ["int_iszero_number_of_Pls","int_iszero_number_of_1", |
|
475 "int_neg_number_of_Min"])@ |
|
476 (map (fn s => thm s RS thm "nlift_bool") |
|
477 ["int_nonzero_number_of_Min","int_not_neg_number_of_Pls"]); |
|
478 |
|
479 val intarith = map thm ["int_number_of_add_sym", "int_number_of_minus_sym", |
|
480 "int_number_of_diff_sym", "int_number_of_mult_sym"]; |
|
481 val natarith = map thm ["add_nat_number_of", "diff_nat_number_of", |
|
482 "mult_nat_number_of", "eq_nat_number_of", |
|
483 "less_nat_number_of"] |
|
484 val powerarith = |
|
485 (map thm ["nat_number_of", "zpower_number_of_even", |
|
486 "zpower_Pls", "zpower_Min"]) @ |
|
487 [(Tactic.simplify true [thm "zero_eq_Numeral0_nring", |
|
488 thm "one_eq_Numeral1_nring"] |
|
489 (thm "zpower_number_of_odd"))] |
|
490 |
|
491 val comp_arith = binarith @ intarith @ intarithrel @ natarith |
|
492 @ powerarith @[thm"not_false_eq_true", thm "not_true_eq_false"]; |
|
493 |
|
494 val comp_ss = HOL_basic_ss addsimps comp_arith addsimps simp_thms; |
|
495 *) |
|
496 fun raw_arith_tac ex i st = |
863 fun raw_arith_tac ex i st = |
|
864 (* FIXME: K true should be replaced by a sensible test (perhaps "isSome o |
|
865 decomp sg"?) to speed things up in case there are lots of irrelevant |
|
866 terms involved; elimination of min/max can be optimized: |
|
867 (max m n + k <= r) = (m+k <= r & n+k <= r) |
|
868 (l <= min m n + k) = (l <= m+k & l <= n+k) |
|
869 *) |
497 refute_tac (K true) |
870 refute_tac (K true) |
498 (REPEAT_DETERM o split_tac (#splits (ArithTheoryData.get (Thm.theory_of_thm st)))) |
871 (* Splitting is also done inside fast_arith_tac, but not completely -- *) |
499 (* (REPEAT o |
872 (* split_tac may use split theorems that have not been implemented in *) |
500 (fn i =>(split_tac (#splits (ArithTheoryData.get(Thm.theory_of_thm st))) i) |
873 (* fast_arith_tac (cf. pre_decomp and split_once_items above). *) |
501 THEN (simp_tac comp_ss i))) *) |
874 (* Therefore splitting outside of fast_arith_tac may allow us to prove *) |
502 ((REPEAT_DETERM o etac linorder_neqE) THEN' fast_ex_arith_tac ex) |
875 (* some goals that fast_arith_tac alone would fail on. *) |
503 i st; |
876 (REPEAT_DETERM o split_tac (#splits (ArithTheoryData.get (Thm.theory_of_thm st)))) |
|
877 (fast_ex_arith_tac ex) |
|
878 i st; |
504 |
879 |
505 fun presburger_tac i st = |
880 fun presburger_tac i st = |
506 (case ArithTheoryData.get (Thm.theory_of_thm st) of |
881 (case ArithTheoryData.get (Thm.theory_of_thm st) of |
507 {presburger = SOME tac, ...} => |
882 {presburger = SOME tac, ...} => |
508 (warning "Trying full Presburger arithmetic ..."; tac i st) |
883 (warning "Trying full Presburger arithmetic ..."; tac i st) |
509 | _ => no_tac st); |
884 | _ => no_tac st); |
510 |
885 |
511 in |
886 in |
512 |
887 |
513 val simple_arith_tac = FIRST' [fast_arith_tac, |
888 val simple_arith_tac = FIRST' [fast_arith_tac, |
514 ObjectLogic.atomize_tac THEN' raw_arith_tac true]; |
889 ObjectLogic.atomize_tac THEN' raw_arith_tac true]; |
515 |
890 |
516 val arith_tac = FIRST' [fast_arith_tac, |
891 val arith_tac = FIRST' [fast_arith_tac, |
517 ObjectLogic.atomize_tac THEN' raw_arith_tac true, |
892 ObjectLogic.atomize_tac THEN' raw_arith_tac true, |
518 presburger_tac]; |
893 presburger_tac]; |
519 |
894 |
520 val silent_arith_tac = FIRST' [fast_arith_tac, |
895 val silent_arith_tac = FIRST' [fast_arith_tac, |
521 ObjectLogic.atomize_tac THEN' raw_arith_tac false, |
896 ObjectLogic.atomize_tac THEN' raw_arith_tac false, |
522 presburger_tac]; |
897 presburger_tac]; |
523 |
898 |
524 fun arith_method prems = |
899 fun arith_method prems = |
525 Method.METHOD (fn facts => HEADGOAL (Method.insert_tac (prems @ facts) THEN' arith_tac)); |
900 Method.METHOD (fn facts => HEADGOAL (Method.insert_tac (prems @ facts) THEN' arith_tac)); |
526 |
901 |
527 end; |
902 end; |
528 |
903 |
529 (* antisymmetry: |
904 (* antisymmetry: |
530 combines x <= y (or ~(y < x)) and y <= x (or ~(x < y)) into x = y |
905 combines x <= y (or ~(y < x)) and y <= x (or ~(x < y)) into x = y |