(* Title: src/Provers/preorder.ML
Author: Oliver Kutter, TU Muenchen
Reasoner for simple transitivity and quasi orders.
*)
(*
The package provides tactics trans_tac and quasi_tac that use
premises of the form
t = u, t ~= u, t < u and t <= u
to
- either derive a contradiction, in which case the conclusion can be
any term,
- or prove the concluson, which must be of the form t ~= u, t < u or
t <= u.
Details:
1. trans_tac:
Only premises of form t <= u are used and the conclusion must be of
the same form. The conclusion is proved, if possible, by a chain of
transitivity from the assumptions.
2. quasi_tac:
<= is assumed to be a quasi order and < its strict relative, defined
as t < u == t <= u & t ~= u. Again, the conclusion is proved from
the assumptions.
Note that the presence of a strict relation is not necessary for
quasi_tac. Configure decomp_quasi to ignore < and ~=. A list of
required theorems for both situations is given below.
*)
signature LESS_ARITH =
sig
(* Transitivity of <=
Note that transitivities for < hold for partial orders only. *)
val le_trans: thm (* [| x <= y; y <= z |] ==> x <= z *)
(* Additional theorem for quasi orders *)
val le_refl: thm (* x <= x *)
val eqD1: thm (* x = y ==> x <= y *)
val eqD2: thm (* x = y ==> y <= x *)
(* Additional theorems for premises of the form x < y *)
val less_reflE: thm (* x < x ==> P *)
val less_imp_le : thm (* x < y ==> x <= y *)
(* Additional theorems for premises of the form x ~= y *)
val le_neq_trans : thm (* [| x <= y ; x ~= y |] ==> x < y *)
val neq_le_trans : thm (* [| x ~= y ; x <= y |] ==> x < y *)
(* Additional theorem for goals of form x ~= y *)
val less_imp_neq : thm (* x < y ==> x ~= y *)
(* Analysis of premises and conclusion *)
(* decomp_x (`x Rel y') should yield SOME (x, Rel, y)
where Rel is one of "<", "<=", "=" and "~=",
other relation symbols cause an error message *)
(* decomp_trans is used by trans_tac, it may only return Rel = "<=" *)
val decomp_trans: theory -> term -> (term * string * term) option
(* decomp_quasi is used by quasi_tac *)
val decomp_quasi: theory -> term -> (term * string * term) option
end;
signature QUASI_TAC =
sig
val trans_tac: Proof.context -> int -> tactic
val quasi_tac: Proof.context -> int -> tactic
end;
functor Quasi_Tac(Less: LESS_ARITH): QUASI_TAC =
struct
(* Internal datatype for the proof *)
datatype proof
= Asm of int
| Thm of proof list * thm;
exception Cannot;
(* Internal exception, raised if conclusion cannot be derived from
assumptions. *)
exception Contr of proof;
(* Internal exception, raised if contradiction ( x < x ) was derived *)
fun prove asms =
let
fun pr (Asm i) = nth asms i
| pr (Thm (prfs, thm)) = map pr prfs MRS thm;
in pr end;
(* Internal datatype for inequalities *)
datatype less
= Less of term * term * proof
| Le of term * term * proof
| NotEq of term * term * proof;
(* Misc functions for datatype less *)
fun lower (Less (x, _, _)) = x
| lower (Le (x, _, _)) = x
| lower (NotEq (x,_,_)) = x;
fun upper (Less (_, y, _)) = y
| upper (Le (_, y, _)) = y
| upper (NotEq (_,y,_)) = y;
fun getprf (Less (_, _, p)) = p
| getprf (Le (_, _, p)) = p
| getprf (NotEq (_,_, p)) = p;
(* ************************************************************************ *)
(* *)
(* mkasm_trans sign (t, n) : theory -> (Term.term * int) -> less *)
(* *)
(* Tuple (t, n) (t an assumption, n its index in the assumptions) is *)
(* translated to an element of type less. *)
(* Only assumptions of form x <= y are used, all others are ignored *)
(* *)
(* ************************************************************************ *)
fun mkasm_trans thy (t, n) =
case Less.decomp_trans thy t of
SOME (x, rel, y) =>
(case rel of
"<=" => [Le (x, y, Asm n)]
| _ => error ("trans_tac: unknown relation symbol ``" ^ rel ^
"''returned by decomp_trans."))
| NONE => [];
(* ************************************************************************ *)
(* *)
(* mkasm_quasi sign (t, n) : theory -> (Term.term * int) -> less *)
(* *)
(* Tuple (t, n) (t an assumption, n its index in the assumptions) is *)
(* translated to an element of type less. *)
(* Quasi orders only. *)
(* *)
(* ************************************************************************ *)
fun mkasm_quasi thy (t, n) =
case Less.decomp_quasi thy t of
SOME (x, rel, y) => (case rel of
"<" => if (x aconv y) then raise Contr (Thm ([Asm n], Less.less_reflE))
else [Less (x, y, Asm n)]
| "<=" => [Le (x, y, Asm n)]
| "=" => [Le (x, y, Thm ([Asm n], Less.eqD1)),
Le (y, x, Thm ([Asm n], Less.eqD2))]
| "~=" => if (x aconv y) then
raise Contr (Thm ([(Thm ([(Thm ([], Less.le_refl)) ,(Asm n)], Less.le_neq_trans))], Less.less_reflE))
else [ NotEq (x, y, Asm n),
NotEq (y, x,Thm ( [Asm n], @{thm not_sym}))]
| _ => error ("quasi_tac: unknown relation symbol ``" ^ rel ^
"''returned by decomp_quasi."))
| NONE => [];
(* ************************************************************************ *)
(* *)
(* mkconcl_trans sign t : theory -> Term.term -> less *)
(* *)
(* Translates conclusion t to an element of type less. *)
(* Only for Conclusions of form x <= y or x < y. *)
(* *)
(* ************************************************************************ *)
fun mkconcl_trans thy t =
case Less.decomp_trans thy t of
SOME (x, rel, y) => (case rel of
"<=" => (Le (x, y, Asm ~1), Asm 0)
| _ => raise Cannot)
| NONE => raise Cannot;
(* ************************************************************************ *)
(* *)
(* mkconcl_quasi sign t : theory -> Term.term -> less *)
(* *)
(* Translates conclusion t to an element of type less. *)
(* Quasi orders only. *)
(* *)
(* ************************************************************************ *)
fun mkconcl_quasi thy t =
case Less.decomp_quasi thy t of
SOME (x, rel, y) => (case rel of
"<" => ([Less (x, y, Asm ~1)], Asm 0)
| "<=" => ([Le (x, y, Asm ~1)], Asm 0)
| "~=" => ([NotEq (x,y, Asm ~1)], Asm 0)
| _ => raise Cannot)
| NONE => raise Cannot;
(* ******************************************************************* *)
(* *)
(* mergeLess (less1,less2): less * less -> less *)
(* *)
(* Merge to elements of type less according to the following rules *)
(* *)
(* x <= y && y <= z ==> x <= z *)
(* x <= y && x ~= y ==> x < y *)
(* x ~= y && x <= y ==> x < y *)
(* *)
(* ******************************************************************* *)
fun mergeLess (Le (x, _, p) , Le (_ , z, q)) =
Le (x, z, Thm ([p,q] , Less.le_trans))
| mergeLess (Le (x, z, p) , NotEq (x', z', q)) =
if (x aconv x' andalso z aconv z' )
then Less (x, z, Thm ([p,q] , Less.le_neq_trans))
else error "quasi_tac: internal error le_neq_trans"
| mergeLess (NotEq (x, z, p) , Le (x' , z', q)) =
if (x aconv x' andalso z aconv z')
then Less (x, z, Thm ([p,q] , Less.neq_le_trans))
else error "quasi_tac: internal error neq_le_trans"
| mergeLess (_, _) =
error "quasi_tac: internal error: undefined case";
(* ******************************************************************** *)
(* tr checks for valid transitivity step *)
(* ******************************************************************** *)
infix tr;
fun (Le (_, y, _)) tr (Le (x', _, _)) = ( y aconv x' )
| _ tr _ = false;
(* ******************************************************************* *)
(* *)
(* transPath (Lesslist, Less): (less list * less) -> less *)
(* *)
(* If a path represented by a list of elements of type less is found, *)
(* this needs to be contracted to a single element of type less. *)
(* Prior to each transitivity step it is checked whether the step is *)
(* valid. *)
(* *)
(* ******************************************************************* *)
fun transPath ([],lesss) = lesss
| transPath (x::xs,lesss) =
if lesss tr x then transPath (xs, mergeLess(lesss,x))
else error "trans/quasi_tac: internal error transpath";
(* ******************************************************************* *)
(* *)
(* less1 subsumes less2 : less -> less -> bool *)
(* *)
(* subsumes checks whether less1 implies less2 *)
(* *)
(* ******************************************************************* *)
infix subsumes;
fun (Le (x, y, _)) subsumes (Le (x', y', _)) =
(x aconv x' andalso y aconv y')
| (Le _) subsumes (Less _) =
error "trans/quasi_tac: internal error: Le cannot subsume Less"
| (NotEq(x,y,_)) subsumes (NotEq(x',y',_)) = x aconv x' andalso y aconv y' orelse x aconv y' andalso y aconv x'
| _ subsumes _ = false;
(* ******************************************************************* *)
(* *)
(* triv_solv less1 : less -> proof option *)
(* *)
(* Solves trivial goal x <= x. *)
(* *)
(* ******************************************************************* *)
fun triv_solv (Le (x, x', _)) =
if x aconv x' then SOME (Thm ([], Less.le_refl))
else NONE
| triv_solv _ = NONE;
(* ********************************************************************* *)
(* Graph functions *)
(* ********************************************************************* *)
(* *********************************************************** *)
(* Functions for constructing graphs *)
(* *********************************************************** *)
fun addEdge (v,d,[]) = [(v,d)]
| addEdge (v,d,((u,dl)::el)) = if v aconv u then ((v,d@dl)::el)
else (u,dl):: (addEdge(v,d,el));
(* ********************************************************************** *)
(* *)
(* mkQuasiGraph constructs from a list of objects of type less a graph g, *)
(* by taking all edges that are candidate for a <=, and a list neqE, by *)
(* taking all edges that are candiate for a ~= *)
(* *)
(* ********************************************************************** *)
fun mkQuasiGraph [] = ([],[])
| mkQuasiGraph lessList =
let
fun buildGraphs ([],leG, neqE) = (leG, neqE)
| buildGraphs (l::ls, leG, neqE) = case l of
(Less (x,y,p)) =>
let
val leEdge = Le (x,y, Thm ([p], Less.less_imp_le))
val neqEdges = [ NotEq (x,y, Thm ([p], Less.less_imp_neq)),
NotEq (y,x, Thm ( [Thm ([p], Less.less_imp_neq)], @{thm not_sym}))]
in
buildGraphs (ls, addEdge(y,[],(addEdge (x,[(y,leEdge)],leG))), neqEdges@neqE)
end
| (Le (x,y,p)) => buildGraphs (ls, addEdge(y,[],(addEdge (x,[(y,l)],leG))), neqE)
| _ => buildGraphs (ls, leG, l::neqE) ;
in buildGraphs (lessList, [], []) end;
(* ********************************************************************** *)
(* *)
(* mkGraph constructs from a list of objects of type less a graph g *)
(* Used for plain transitivity chain reasoning. *)
(* *)
(* ********************************************************************** *)
fun mkGraph [] = []
| mkGraph lessList =
let
fun buildGraph ([],g) = g
| buildGraph (l::ls, g) = buildGraph (ls, (addEdge ((lower l),[((upper l),l)],g)))
in buildGraph (lessList, []) end;
(* *********************************************************************** *)
(* *)
(* adjacent g u : (''a * 'b list ) list -> ''a -> 'b list *)
(* *)
(* List of successors of u in graph g *)
(* *)
(* *********************************************************************** *)
fun adjacent eq_comp ((v,adj)::el) u =
if eq_comp (u, v) then adj else adjacent eq_comp el u
| adjacent _ [] _ = []
(* *********************************************************************** *)
(* *)
(* dfs eq_comp g u v: *)
(* ('a * 'a -> bool) -> ('a *( 'a * less) list) list -> *)
(* 'a -> 'a -> (bool * ('a * less) list) *)
(* *)
(* Depth first search of v from u. *)
(* Returns (true, path(u, v)) if successful, otherwise (false, []). *)
(* *)
(* *********************************************************************** *)
fun dfs eq_comp g u v =
let
val pred = Unsynchronized.ref [];
val visited = Unsynchronized.ref [];
fun been_visited v = exists (fn w => eq_comp (w, v)) (!visited)
fun dfs_visit u' =
let val _ = visited := u' :: (!visited)
fun update (x,l) = let val _ = pred := (x,l) ::(!pred) in () end;
in if been_visited v then ()
else (List.app (fn (v',l) => if been_visited v' then () else (
update (v',l);
dfs_visit v'; ()) )) (adjacent eq_comp g u')
end
in
dfs_visit u;
if (been_visited v) then (true, (!pred)) else (false , [])
end;
(* ************************************************************************ *)
(* *)
(* Begin: Quasi Order relevant functions *)
(* *)
(* *)
(* ************************************************************************ *)
(* ************************************************************************ *)
(* *)
(* findPath x y g: Term.term -> Term.term -> *)
(* (Term.term * (Term.term * less list) list) -> *)
(* (bool, less list) *)
(* *)
(* Searches a path from vertex x to vertex y in Graph g, returns true and *)
(* the list of edges forming the path, if a path is found, otherwise false *)
(* and nil. *)
(* *)
(* ************************************************************************ *)
fun findPath x y g =
let
val (found, tmp) = dfs (op aconv) g x y ;
val pred = map snd tmp;
fun path x y =
let
(* find predecessor u of node v and the edge u -> v *)
fun lookup v [] = raise Cannot
| lookup v (e::es) = if (upper e) aconv v then e else lookup v es;
(* traverse path backwards and return list of visited edges *)
fun rev_path v =
let val l = lookup v pred
val u = lower l;
in
if u aconv x then [l] else (rev_path u) @ [l]
end
in rev_path y end;
in
if found then (
if x aconv y then (found,[(Le (x, y, (Thm ([], Less.le_refl))))])
else (found, (path x y) ))
else (found,[])
end;
(* ************************************************************************ *)
(* *)
(* findQuasiProof (leqG, neqE) subgoal: *)
(* (Term.term * (Term.term * less list) list) * less list -> less -> proof *)
(* *)
(* Constructs a proof for subgoal by searching a special path in leqG and *)
(* neqE. Raises Cannot if construction of the proof fails. *)
(* *)
(* ************************************************************************ *)
(* As the conlusion can be either of form x <= y, x < y or x ~= y we have *)
(* three cases to deal with. Finding a transitivity path from x to y with label *)
(* 1. <= *)
(* This is simply done by searching any path from x to y in the graph leG. *)
(* The graph leG contains only edges with label <=. *)
(* *)
(* 2. < *)
(* A path from x to y with label < can be found by searching a path with *)
(* label <= from x to y in the graph leG and merging the path x <= y with *)
(* a parallel edge x ~= y resp. y ~= x to x < y. *)
(* *)
(* 3. ~= *)
(* If the conclusion is of form x ~= y, we can find a proof either directly, *)
(* if x ~= y or y ~= x are among the assumptions, or by constructing x ~= y if *)
(* x < y or y < x follows from the assumptions. *)
fun findQuasiProof (leG, neqE) subgoal =
case subgoal of (Le (x,y, _)) => (
let
val (xyLefound,xyLePath) = findPath x y leG
in
if xyLefound then (
let
val Le_x_y = (transPath (tl xyLePath, hd xyLePath))
in getprf Le_x_y end )
else raise Cannot
end )
| (Less (x,y,_)) => (
let
fun findParallelNeq [] = NONE
| findParallelNeq (e::es) =
if (x aconv (lower e) andalso y aconv (upper e)) then SOME e
else if (y aconv (lower e) andalso x aconv (upper e))
then SOME (NotEq (x,y, (Thm ([getprf e], @{thm not_sym}))))
else findParallelNeq es;
in
(* test if there is a edge x ~= y respectivly y ~= x and *)
(* if it possible to find a path x <= y in leG, thus we can conclude x < y *)
(case findParallelNeq neqE of (SOME e) =>
let
val (xyLeFound,xyLePath) = findPath x y leG
in
if xyLeFound then (
let
val Le_x_y = (transPath (tl xyLePath, hd xyLePath))
val Less_x_y = mergeLess (e, Le_x_y)
in getprf Less_x_y end
) else raise Cannot
end
| _ => raise Cannot)
end )
| (NotEq (x,y,_)) =>
(* First check if a single premiss is sufficient *)
(case (find_first (fn fact => fact subsumes subgoal) neqE, subgoal) of
(SOME (NotEq (x, y, p)), NotEq (x', y', _)) =>
if (x aconv x' andalso y aconv y') then p
else Thm ([p], @{thm not_sym})
| _ => raise Cannot
)
(* ************************************************************************ *)
(* *)
(* End: Quasi Order relevant functions *)
(* *)
(* *)
(* ************************************************************************ *)
(* *********************************************************************** *)
(* *)
(* solveLeTrans sign (asms,concl) : *)
(* theory -> less list * Term.term -> proof list *)
(* *)
(* Solves *)
(* *)
(* *********************************************************************** *)
fun solveLeTrans thy (asms, concl) =
let
val g = mkGraph asms
in
let
val (subgoal, prf) = mkconcl_trans thy concl
val (found, path) = findPath (lower subgoal) (upper subgoal) g
in
if found then [getprf (transPath (tl path, hd path))]
else raise Cannot
end
end;
(* *********************************************************************** *)
(* *)
(* solveQuasiOrder sign (asms,concl) : *)
(* theory -> less list * Term.term -> proof list *)
(* *)
(* Find proof if possible for quasi order. *)
(* *)
(* *********************************************************************** *)
fun solveQuasiOrder thy (asms, concl) =
let
val (leG, neqE) = mkQuasiGraph asms
in
let
val (subgoals, prf) = mkconcl_quasi thy concl
fun solve facts less =
(case triv_solv less of NONE => findQuasiProof (leG, neqE) less
| SOME prf => prf )
in map (solve asms) subgoals end
end;
(* ************************************************************************ *)
(* *)
(* Tactics *)
(* *)
(* - trans_tac *)
(* - quasi_tac, solves quasi orders *)
(* ************************************************************************ *)
(* trans_tac - solves transitivity chains over <= *)
fun trans_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
let
val thy = Proof_Context.theory_of ctxt;
val rfrees = map Free (Term.rename_wrt_term A (Logic.strip_params A));
val Hs = map (fn H => subst_bounds (rfrees, H)) (Logic.strip_assums_hyp A);
val C = subst_bounds (rfrees, Logic.strip_assums_concl A);
val lesss = flat (map_index (mkasm_trans thy o swap) Hs);
val prfs = solveLeTrans thy (lesss, C);
val (subgoal, prf) = mkconcl_trans thy C;
in
Subgoal.FOCUS (fn {context = ctxt', prems, ...} =>
let val thms = map (prove prems) prfs
in resolve_tac ctxt' [prove thms prf] 1 end) ctxt n st
end
handle Contr p =>
Subgoal.FOCUS (fn {context = ctxt', prems, ...} =>
resolve_tac ctxt' [prove prems p] 1) ctxt n st
| Cannot => Seq.empty);
(* quasi_tac - solves quasi orders *)
fun quasi_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
let
val thy = Proof_Context.theory_of ctxt
val rfrees = map Free (Term.rename_wrt_term A (Logic.strip_params A));
val Hs = map (fn H => subst_bounds (rfrees, H)) (Logic.strip_assums_hyp A);
val C = subst_bounds (rfrees, Logic.strip_assums_concl A);
val lesss = flat (map_index (mkasm_quasi thy o swap) Hs);
val prfs = solveQuasiOrder thy (lesss, C);
val (subgoals, prf) = mkconcl_quasi thy C;
in
Subgoal.FOCUS (fn {context = ctxt', prems, ...} =>
let val thms = map (prove prems) prfs
in resolve_tac ctxt' [prove thms prf] 1 end) ctxt n st
end
handle Contr p =>
(Subgoal.FOCUS (fn {context = ctxt', prems, ...} =>
resolve_tac ctxt' [prove prems p] 1) ctxt n st handle General.Subscript => Seq.empty)
| Cannot => Seq.empty
| General.Subscript => Seq.empty);
end;