2 Title: Transitivity reasoner for transitive closures of relations
3 Author: Oliver Kutter, TU Muenchen
8 The packages provides tactics trancl_tac and rtrancl_tac that prove
11 (x,y) : r^+ and (x,y) : r^* (rtrancl_tac only)
13 from premises of the form
15 (x,y) : r, (x,y) : r^+ and (x,y) : r^* (rtrancl_tac only)
17 by reflexivity and transitivity. The relation r is determined by inspecting
20 The package is implemented as an ML functor and thus not limited to
21 particular constructs for transitive and reflexive-transitive
22 closures, neither need relations be represented as sets of pairs. In
23 order to instantiate the package for transitive closure only, supply
24 dummy theorems to the additional rules for reflexive-transitive
25 closures, and don't use rtrancl_tac!
29 signature TRANCL_ARITH =
32 (* theorems for transitive closure *)
34 val r_into_trancl : thm
35 (* (a,b) : r ==> (a,b) : r^+ *)
36 val trancl_trans : thm
37 (* [| (a,b) : r^+ ; (b,c) : r^+ |] ==> (a,c) : r^+ *)
39 (* additional theorems for reflexive-transitive closure *)
41 val rtrancl_refl : thm
43 val r_into_rtrancl : thm
44 (* (a,b) : r ==> (a,b) : r^* *)
45 val trancl_into_rtrancl : thm
46 (* (a,b) : r^+ ==> (a,b) : r^* *)
47 val rtrancl_trancl_trancl : thm
48 (* [| (a,b) : r^* ; (b,c) : r^+ |] ==> (a,c) : r^+ *)
49 val trancl_rtrancl_trancl : thm
50 (* [| (a,b) : r^+ ; (b,c) : r^* |] ==> (a,c) : r^+ *)
51 val rtrancl_trans : thm
52 (* [| (a,b) : r^* ; (b,c) : r^* |] ==> (a,c) : r^* *)
54 (* decomp: decompose a premise or conclusion
56 Returns one of the following:
58 NONE if not an instance of a relation,
59 SOME (x, y, r, s) if instance of a relation, where
60 x: left hand side argument, y: right hand side argument,
62 s: the kind of closure, one of
63 "r": the relation itself,
64 "r^+": transitive closure of the relation,
65 "r^*": reflexive-transitive closure of the relation
68 val decomp: term -> (term * term * term * string) option
72 signature TRANCL_TAC =
74 val trancl_tac: Proof.context -> int -> tactic
75 val rtrancl_tac: Proof.context -> int -> tactic
78 functor Trancl_Tac(Cls: TRANCL_ARITH): TRANCL_TAC =
84 | Thm of proof list * thm;
86 exception Cannot; (* internal exception: raised if no proof can be found *)
88 fun decomp t = Option.map (fn (x, y, rel, r) =>
89 (Envir.beta_eta_contract x, Envir.beta_eta_contract y,
90 Envir.beta_eta_contract rel, r)) (Cls.decomp t);
92 fun prove thy r asms =
95 let val SOME (_, _, r', _) = decomp (concl_of thm)
96 in Drule.cterm_instantiate [(cterm_of thy r', cterm_of thy r)] thm end;
97 fun pr (Asm i) = List.nth (asms, i)
98 | pr (Thm (prfs, thm)) = map pr prfs MRS inst thm
102 (* Internal datatype for inequalities *)
104 = Trans of term * term * proof (* R^+ *)
105 | RTrans of term * term * proof; (* R^* *)
107 (* Misc functions for datatype rel *)
108 fun lower (Trans (x, _, _)) = x
109 | lower (RTrans (x,_,_)) = x;
111 fun upper (Trans (_, y, _)) = y
112 | upper (RTrans (_,y,_)) = y;
114 fun getprf (Trans (_, _, p)) = p
115 | getprf (RTrans (_,_, p)) = p;
117 (* ************************************************************************ *)
119 (* mkasm_trancl Rel (t,n): term -> (term , int) -> rel list *)
121 (* Analyse assumption t with index n with respect to relation Rel: *)
122 (* If t is of the form "(x, y) : Rel" (or Rel^+), translate to *)
123 (* an object (singleton list) of internal datatype rel. *)
124 (* Otherwise return empty list. *)
126 (* ************************************************************************ *)
128 fun mkasm_trancl Rel (t, n) =
130 SOME (x, y, rel,r) => if rel aconv Rel then
133 "r" => [Trans (x,y, Thm([Asm n], Cls.r_into_trancl))]
134 | "r+" => [Trans (x,y, Asm n)]
136 | _ => error ("trancl_tac: unknown relation symbol"))
140 (* ************************************************************************ *)
142 (* mkasm_rtrancl Rel (t,n): term -> (term , int) -> rel list *)
144 (* Analyse assumption t with index n with respect to relation Rel: *)
145 (* If t is of the form "(x, y) : Rel" (or Rel^+ or Rel^* ), translate to *)
146 (* an object (singleton list) of internal datatype rel. *)
147 (* Otherwise return empty list. *)
149 (* ************************************************************************ *)
151 fun mkasm_rtrancl Rel (t, n) =
153 SOME (x, y, rel, r) => if rel aconv Rel then
155 "r" => [ Trans (x,y, Thm([Asm n], Cls.r_into_trancl))]
156 | "r+" => [ Trans (x,y, Asm n)]
157 | "r*" => [ RTrans(x,y, Asm n)]
158 | _ => error ("rtrancl_tac: unknown relation symbol" ))
162 (* ************************************************************************ *)
164 (* mkconcl_trancl t: term -> (term, rel, proof) *)
165 (* mkconcl_rtrancl t: term -> (term, rel, proof) *)
167 (* Analyse conclusion t: *)
168 (* - must be of form "(x, y) : r^+ (or r^* for rtrancl) *)
170 (* - conclusion in internal form *)
173 (* ************************************************************************ *)
175 fun mkconcl_trancl t =
177 SOME (x, y, rel, r) => (case r of
178 "r+" => (rel, Trans (x,y, Asm ~1), Asm 0)
180 | NONE => raise Cannot;
182 fun mkconcl_rtrancl t =
184 SOME (x, y, rel,r ) => (case r of
185 "r+" => (rel, Trans (x,y, Asm ~1), Asm 0)
186 | "r*" => (rel, RTrans (x,y, Asm ~1), Asm 0)
188 | NONE => raise Cannot;
190 (* ************************************************************************ *)
192 (* makeStep (r1, r2): rel * rel -> rel *)
194 (* Apply transitivity to r1 and r2, obtaining a new element of r^+ or r^*, *)
195 (* according the following rules: *)
197 (* ( (a, b) : r^+ , (b,c) : r^+ ) --> (a,c) : r^+ *)
198 (* ( (a, b) : r^* , (b,c) : r^+ ) --> (a,c) : r^+ *)
199 (* ( (a, b) : r^+ , (b,c) : r^* ) --> (a,c) : r^+ *)
200 (* ( (a, b) : r^* , (b,c) : r^* ) --> (a,c) : r^* *)
202 (* ************************************************************************ *)
204 fun makeStep (Trans (a,_,p), Trans(_,c,q)) = Trans (a,c, Thm ([p,q], Cls.trancl_trans))
205 (* refl. + trans. cls. rules *)
206 | makeStep (RTrans (a,_,p), Trans(_,c,q)) = Trans (a,c, Thm ([p,q], Cls.rtrancl_trancl_trancl))
207 | makeStep (Trans (a,_,p), RTrans(_,c,q)) = Trans (a,c, Thm ([p,q], Cls.trancl_rtrancl_trancl))
208 | makeStep (RTrans (a,_,p), RTrans(_,c,q)) = RTrans (a,c, Thm ([p,q], Cls.rtrancl_trans));
210 (* ******************************************************************* *)
212 (* transPath (Clslist, Cls): (rel list * rel) -> rel *)
214 (* If a path represented by a list of elements of type rel is found, *)
215 (* this needs to be contracted to a single element of type rel. *)
216 (* Prior to each transitivity step it is checked whether the step is *)
219 (* ******************************************************************* *)
221 fun transPath ([],acc) = acc
222 | transPath (x::xs,acc) = transPath (xs, makeStep(acc,x))
224 (* ********************************************************************* *)
225 (* Graph functions *)
226 (* ********************************************************************* *)
228 (* *********************************************************** *)
229 (* Functions for constructing graphs *)
230 (* *********************************************************** *)
232 fun addEdge (v,d,[]) = [(v,d)]
233 | addEdge (v,d,((u,dl)::el)) = if v aconv u then ((v,d@dl)::el)
234 else (u,dl):: (addEdge(v,d,el));
236 (* ********************************************************************** *)
238 (* mkGraph constructs from a list of objects of type rel a graph g *)
239 (* and a list of all edges with label r+. *)
241 (* ********************************************************************** *)
243 fun mkGraph [] = ([],[])
246 fun buildGraph ([],g,zs) = (g,zs)
247 | buildGraph (x::xs, g, zs) =
248 case x of (Trans (_,_,_)) =>
249 buildGraph (xs, addEdge((upper x), [],(addEdge ((lower x),[((upper x),x)],g))), x::zs)
250 | _ => buildGraph (xs, addEdge((upper x), [],(addEdge ((lower x),[((upper x),x)],g))), zs)
251 in buildGraph (ys, [], []) end;
253 (* *********************************************************************** *)
255 (* adjacent g u : (''a * 'b list ) list -> ''a -> 'b list *)
257 (* List of successors of u in graph g *)
259 (* *********************************************************************** *)
261 fun adjacent eq_comp ((v,adj)::el) u =
262 if eq_comp (u, v) then adj else adjacent eq_comp el u
263 | adjacent _ [] _ = []
265 (* *********************************************************************** *)
267 (* dfs eq_comp g u v: *)
268 (* ('a * 'a -> bool) -> ('a *( 'a * rel) list) list -> *)
269 (* 'a -> 'a -> (bool * ('a * rel) list) *)
271 (* Depth first search of v from u. *)
272 (* Returns (true, path(u, v)) if successful, otherwise (false, []). *)
274 (* *********************************************************************** *)
276 fun dfs eq_comp g u v =
278 val pred = Unsynchronized.ref [];
279 val visited = Unsynchronized.ref [];
281 fun been_visited v = exists (fn w => eq_comp (w, v)) (!visited)
284 let val _ = visited := u' :: (!visited)
286 fun update (x,l) = let val _ = pred := (x,l) ::(!pred) in () end;
288 in if been_visited v then ()
289 else (app (fn (v',l) => if been_visited v' then () else (
291 dfs_visit v'; ()) )) (adjacent eq_comp g u')
295 if (been_visited v) then (true, (!pred)) else (false , [])
298 (* *********************************************************************** *)
301 (* (''a * ''a list) list -> (''a * ''a list) list *)
303 (* Computes transposed graph g' from g *)
304 (* by reversing all edges u -> v to v -> u *)
306 (* *********************************************************************** *)
308 fun transpose eq_comp g =
310 (* Compute list of reversed edges for each adjacency list *)
311 fun flip (u,(v,l)::el) = (v,(u,l)) :: flip (u,el)
314 (* Compute adjacency list for node u from the list of edges
315 and return a likewise reduced list of edges. The list of edges
316 is searches for edges starting from u, and these edges are removed. *)
317 fun gather (u,(v,w)::el) =
319 val (adj,edges) = gather (u,el)
321 if eq_comp (u, v) then (w::adj,edges)
322 else (adj,(v,w)::edges)
324 | gather (_,[]) = ([],[])
326 (* For every node in the input graph, call gather to find all reachable
327 nodes in the list of edges *)
328 fun assemble ((u,_)::el) edges =
329 let val (adj,edges) = gather (u,edges)
330 in (u,adj) :: assemble el edges
334 (* Compute, for each adjacency list, the list with reversed edges,
335 and concatenate these lists. *)
336 val flipped = maps flip g
338 in assemble g flipped end
340 (* *********************************************************************** *)
342 (* dfs_reachable eq_comp g u: *)
343 (* (int * int list) list -> int -> int list *)
345 (* Computes list of all nodes reachable from u in g. *)
347 (* *********************************************************************** *)
349 fun dfs_reachable eq_comp g u =
351 (* List of vertices which have been visited. *)
352 val visited = Unsynchronized.ref [];
354 fun been_visited v = exists (fn w => eq_comp (w, v)) (!visited)
358 val _ = visited := u :: !visited
360 List.foldr (fn ((v,l),ds) => if been_visited v then ds
361 else v :: dfs_visit g v @ ds)
362 [] (adjacent eq_comp g u)
365 in u :: dfs_visit g u end;
367 (* *********************************************************************** *)
369 (* dfs_term_reachable g u: *)
370 (* (term * term list) list -> term -> term list *)
372 (* Computes list of all nodes reachable from u in g. *)
374 (* *********************************************************************** *)
376 fun dfs_term_reachable g u = dfs_reachable (op aconv) g u;
378 (* ************************************************************************ *)
380 (* findPath x y g: Term.term -> Term.term -> *)
381 (* (Term.term * (Term.term * rel list) list) -> *)
382 (* (bool, rel list) *)
384 (* Searches a path from vertex x to vertex y in Graph g, returns true and *)
385 (* the list of edges if path is found, otherwise false and nil. *)
387 (* ************************************************************************ *)
391 val (found, tmp) = dfs (op aconv) g x y ;
392 val pred = map snd tmp;
396 (* find predecessor u of node v and the edge u -> v *)
398 fun lookup v [] = raise Cannot
399 | lookup v (e::es) = if (upper e) aconv v then e else lookup v es;
401 (* traverse path backwards and return list of visited edges *)
403 let val l = lookup v pred
406 if u aconv x then [l] else (rev_path u) @ [l]
414 if found then ( (found, (path x y) )) else (found,[])
420 (* ************************************************************************ *)
422 (* findRtranclProof g tranclEdges subgoal: *)
423 (* (Term.term * (Term.term * rel list) list) -> rel -> proof list *)
425 (* Searches in graph g a proof for subgoal. *)
427 (* ************************************************************************ *)
429 fun findRtranclProof g tranclEdges subgoal =
430 case subgoal of (RTrans (x,y,_)) => if x aconv y then [Thm ([], Cls.rtrancl_refl)] else (
431 let val (found, path) = findPath (lower subgoal) (upper subgoal) g
434 let val path' = (transPath (tl path, hd path))
437 case path' of (Trans (_,_,p)) => [Thm ([p], Cls.trancl_into_rtrancl )]
438 | _ => [getprf path']
446 | (Trans (x,y,_)) => (
449 val Vx = dfs_term_reachable g x;
450 val g' = transpose (op aconv) g;
451 val Vy = dfs_term_reachable g' y;
453 fun processTranclEdges [] = raise Cannot
454 | processTranclEdges (e::es) =
455 if (upper e) mem Vx andalso (lower e) mem Vx
456 andalso (upper e) mem Vy andalso (lower e) mem Vy
460 if (lower e) aconv x then (
461 if (upper e) aconv y then (
466 val (found,path) = findPath (upper e) y g
470 [getprf (transPath (path, e))]
471 ) else processTranclEdges es
476 else if (upper e) aconv y then (
477 let val (xufound,xupath) = findPath x (lower e) g
482 let val xuRTranclEdge = transPath (tl xupath, hd xupath)
483 val xyTranclEdge = makeStep(xuRTranclEdge,e)
485 in [getprf xyTranclEdge] end
487 ) else processTranclEdges es
493 let val (xufound,xupath) = findPath x (lower e) g
494 val (vyfound,vypath) = findPath (upper e) y g
498 let val xuRTranclEdge = transPath (tl xupath, hd xupath)
499 val vyRTranclEdge = transPath (tl vypath, hd vypath)
500 val xyTranclEdge = makeStep (makeStep(xuRTranclEdge,e),vyRTranclEdge)
502 in [getprf xyTranclEdge] end
504 ) else processTranclEdges es
506 else processTranclEdges es
510 else processTranclEdges es;
511 in processTranclEdges tranclEdges end )
515 fun solveTrancl (asms, concl) =
516 let val (g,_) = mkGraph asms
518 let val (_, subgoal, _) = mkconcl_trancl concl
519 val (found, path) = findPath (lower subgoal) (upper subgoal) g
521 if found then [getprf (transPath (tl path, hd path))]
526 fun solveRtrancl (asms, concl) =
527 let val (g,tranclEdges) = mkGraph asms
528 val (_, subgoal, _) = mkconcl_rtrancl concl
530 findRtranclProof g tranclEdges subgoal
534 fun trancl_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
536 val thy = ProofContext.theory_of ctxt;
537 val Hs = Logic.strip_assums_hyp A;
538 val C = Logic.strip_assums_concl A;
539 val (rel, subgoals, prf) = mkconcl_trancl C;
541 val prems = flat (map_index (mkasm_trancl rel o swap) Hs);
542 val prfs = solveTrancl (prems, C);
544 Subgoal.FOCUS (fn {prems, ...} =>
545 let val thms = map (prove thy rel prems) prfs
546 in rtac (prove thy rel thms prf) 1 end) ctxt n st
548 handle Cannot => Seq.empty);
551 fun rtrancl_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
553 val thy = ProofContext.theory_of ctxt;
554 val Hs = Logic.strip_assums_hyp A;
555 val C = Logic.strip_assums_concl A;
556 val (rel, subgoals, prf) = mkconcl_rtrancl C;
558 val prems = flat (map_index (mkasm_rtrancl rel o swap) Hs);
559 val prfs = solveRtrancl (prems, C);
561 Subgoal.FOCUS (fn {prems, ...} =>
562 let val thms = map (prove thy rel prems) prfs
563 in rtac (prove thy rel thms prf) 1 end) ctxt n st
565 handle Cannot => Seq.empty | Subscript => Seq.empty);