1 (* Title: Provers/quasi.ML
2 Author: Oliver Kutter, TU Muenchen
4 Reasoner for simple transitivity and quasi orders.
9 The package provides tactics trans_tac and quasi_tac that use
12 t = u, t ~= u, t < u and t <= u
15 - either derive a contradiction, in which case the conclusion can be
17 - or prove the concluson, which must be of the form t ~= u, t < u or
23 Only premises of form t <= u are used and the conclusion must be of
24 the same form. The conclusion is proved, if possible, by a chain of
25 transitivity from the assumptions.
28 <= is assumed to be a quasi order and < its strict relative, defined
29 as t < u == t <= u & t ~= u. Again, the conclusion is proved from
31 Note that the presence of a strict relation is not necessary for
32 quasi_tac. Configure decomp_quasi to ignore < and ~=. A list of
33 required theorems for both situations is given below.
36 signature LESS_ARITH =
39 Note that transitivities for < hold for partial orders only. *)
40 val le_trans: thm (* [| x <= y; y <= z |] ==> x <= z *)
42 (* Additional theorem for quasi orders *)
43 val le_refl: thm (* x <= x *)
44 val eqD1: thm (* x = y ==> x <= y *)
45 val eqD2: thm (* x = y ==> y <= x *)
47 (* Additional theorems for premises of the form x < y *)
48 val less_reflE: thm (* x < x ==> P *)
49 val less_imp_le : thm (* x < y ==> x <= y *)
51 (* Additional theorems for premises of the form x ~= y *)
52 val le_neq_trans : thm (* [| x <= y ; x ~= y |] ==> x < y *)
53 val neq_le_trans : thm (* [| x ~= y ; x <= y |] ==> x < y *)
55 (* Additional theorem for goals of form x ~= y *)
56 val less_imp_neq : thm (* x < y ==> x ~= y *)
58 (* Analysis of premises and conclusion *)
59 (* decomp_x (`x Rel y') should yield SOME (x, Rel, y)
60 where Rel is one of "<", "<=", "=" and "~=",
61 other relation symbols cause an error message *)
62 (* decomp_trans is used by trans_tac, it may only return Rel = "<=" *)
63 val decomp_trans: theory -> term -> (term * string * term) option
64 (* decomp_quasi is used by quasi_tac *)
65 val decomp_quasi: theory -> term -> (term * string * term) option
70 val trans_tac: Proof.context -> int -> tactic
71 val quasi_tac: Proof.context -> int -> tactic
74 functor Quasi_Tac(Less: LESS_ARITH): QUASI_TAC =
77 (* Internal datatype for the proof *)
80 | Thm of proof list * thm;
83 (* Internal exception, raised if conclusion cannot be derived from
85 exception Contr of proof;
86 (* Internal exception, raised if contradiction ( x < x ) was derived *)
89 let fun pr (Asm i) = List.nth (asms, i)
90 | pr (Thm (prfs, thm)) = (map pr prfs) MRS thm
93 (* Internal datatype for inequalities *)
95 = Less of term * term * proof
96 | Le of term * term * proof
97 | NotEq of term * term * proof;
99 (* Misc functions for datatype less *)
100 fun lower (Less (x, _, _)) = x
101 | lower (Le (x, _, _)) = x
102 | lower (NotEq (x,_,_)) = x;
104 fun upper (Less (_, y, _)) = y
105 | upper (Le (_, y, _)) = y
106 | upper (NotEq (_,y,_)) = y;
108 fun getprf (Less (_, _, p)) = p
109 | getprf (Le (_, _, p)) = p
110 | getprf (NotEq (_,_, p)) = p;
112 (* ************************************************************************ *)
114 (* mkasm_trans sign (t, n) : theory -> (Term.term * int) -> less *)
116 (* Tuple (t, n) (t an assumption, n its index in the assumptions) is *)
117 (* translated to an element of type less. *)
118 (* Only assumptions of form x <= y are used, all others are ignored *)
120 (* ************************************************************************ *)
122 fun mkasm_trans thy (t, n) =
123 case Less.decomp_trans thy t of
126 "<=" => [Le (x, y, Asm n)]
127 | _ => error ("trans_tac: unknown relation symbol ``" ^ rel ^
128 "''returned by decomp_trans."))
131 (* ************************************************************************ *)
133 (* mkasm_quasi sign (t, n) : theory -> (Term.term * int) -> less *)
135 (* Tuple (t, n) (t an assumption, n its index in the assumptions) is *)
136 (* translated to an element of type less. *)
137 (* Quasi orders only. *)
139 (* ************************************************************************ *)
141 fun mkasm_quasi thy (t, n) =
142 case Less.decomp_quasi thy t of
143 SOME (x, rel, y) => (case rel of
144 "<" => if (x aconv y) then raise Contr (Thm ([Asm n], Less.less_reflE))
145 else [Less (x, y, Asm n)]
146 | "<=" => [Le (x, y, Asm n)]
147 | "=" => [Le (x, y, Thm ([Asm n], Less.eqD1)),
148 Le (y, x, Thm ([Asm n], Less.eqD2))]
149 | "~=" => if (x aconv y) then
150 raise Contr (Thm ([(Thm ([(Thm ([], Less.le_refl)) ,(Asm n)], Less.le_neq_trans))], Less.less_reflE))
151 else [ NotEq (x, y, Asm n),
152 NotEq (y, x,Thm ( [Asm n], thm "not_sym"))]
153 | _ => error ("quasi_tac: unknown relation symbol ``" ^ rel ^
154 "''returned by decomp_quasi."))
158 (* ************************************************************************ *)
160 (* mkconcl_trans sign t : theory -> Term.term -> less *)
162 (* Translates conclusion t to an element of type less. *)
163 (* Only for Conclusions of form x <= y or x < y. *)
165 (* ************************************************************************ *)
168 fun mkconcl_trans thy t =
169 case Less.decomp_trans thy t of
170 SOME (x, rel, y) => (case rel of
171 "<=" => (Le (x, y, Asm ~1), Asm 0)
173 | NONE => raise Cannot;
176 (* ************************************************************************ *)
178 (* mkconcl_quasi sign t : theory -> Term.term -> less *)
180 (* Translates conclusion t to an element of type less. *)
181 (* Quasi orders only. *)
183 (* ************************************************************************ *)
185 fun mkconcl_quasi thy t =
186 case Less.decomp_quasi thy t of
187 SOME (x, rel, y) => (case rel of
188 "<" => ([Less (x, y, Asm ~1)], Asm 0)
189 | "<=" => ([Le (x, y, Asm ~1)], Asm 0)
190 | "~=" => ([NotEq (x,y, Asm ~1)], Asm 0)
192 | NONE => raise Cannot;
195 (* ******************************************************************* *)
197 (* mergeLess (less1,less2): less * less -> less *)
199 (* Merge to elements of type less according to the following rules *)
201 (* x <= y && y <= z ==> x <= z *)
202 (* x <= y && x ~= y ==> x < y *)
203 (* x ~= y && x <= y ==> x < y *)
205 (* ******************************************************************* *)
207 fun mergeLess (Le (x, _, p) , Le (_ , z, q)) =
208 Le (x, z, Thm ([p,q] , Less.le_trans))
209 | mergeLess (Le (x, z, p) , NotEq (x', z', q)) =
210 if (x aconv x' andalso z aconv z' )
211 then Less (x, z, Thm ([p,q] , Less.le_neq_trans))
212 else error "quasi_tac: internal error le_neq_trans"
213 | mergeLess (NotEq (x, z, p) , Le (x' , z', q)) =
214 if (x aconv x' andalso z aconv z')
215 then Less (x, z, Thm ([p,q] , Less.neq_le_trans))
216 else error "quasi_tac: internal error neq_le_trans"
218 error "quasi_tac: internal error: undefined case";
221 (* ******************************************************************** *)
222 (* tr checks for valid transitivity step *)
223 (* ******************************************************************** *)
226 fun (Le (_, y, _)) tr (Le (x', _, _)) = ( y aconv x' )
229 (* ******************************************************************* *)
231 (* transPath (Lesslist, Less): (less list * less) -> less *)
233 (* If a path represented by a list of elements of type less is found, *)
234 (* this needs to be contracted to a single element of type less. *)
235 (* Prior to each transitivity step it is checked whether the step is *)
238 (* ******************************************************************* *)
240 fun transPath ([],lesss) = lesss
241 | transPath (x::xs,lesss) =
242 if lesss tr x then transPath (xs, mergeLess(lesss,x))
243 else error "trans/quasi_tac: internal error transpath";
245 (* ******************************************************************* *)
247 (* less1 subsumes less2 : less -> less -> bool *)
249 (* subsumes checks whether less1 implies less2 *)
251 (* ******************************************************************* *)
254 fun (Le (x, y, _)) subsumes (Le (x', y', _)) =
255 (x aconv x' andalso y aconv y')
256 | (Le _) subsumes (Less _) =
257 error "trans/quasi_tac: internal error: Le cannot subsume Less"
258 | (NotEq(x,y,_)) subsumes (NotEq(x',y',_)) = x aconv x' andalso y aconv y' orelse x aconv y' andalso y aconv x'
259 | _ subsumes _ = false;
261 (* ******************************************************************* *)
263 (* triv_solv less1 : less -> proof option *)
265 (* Solves trivial goal x <= x. *)
267 (* ******************************************************************* *)
269 fun triv_solv (Le (x, x', _)) =
270 if x aconv x' then SOME (Thm ([], Less.le_refl))
272 | triv_solv _ = NONE;
274 (* ********************************************************************* *)
275 (* Graph functions *)
276 (* ********************************************************************* *)
278 (* *********************************************************** *)
279 (* Functions for constructing graphs *)
280 (* *********************************************************** *)
282 fun addEdge (v,d,[]) = [(v,d)]
283 | addEdge (v,d,((u,dl)::el)) = if v aconv u then ((v,d@dl)::el)
284 else (u,dl):: (addEdge(v,d,el));
286 (* ********************************************************************** *)
288 (* mkQuasiGraph constructs from a list of objects of type less a graph g, *)
289 (* by taking all edges that are candidate for a <=, and a list neqE, by *)
290 (* taking all edges that are candiate for a ~= *)
292 (* ********************************************************************** *)
294 fun mkQuasiGraph [] = ([],[])
295 | mkQuasiGraph lessList =
297 fun buildGraphs ([],leG, neqE) = (leG, neqE)
298 | buildGraphs (l::ls, leG, neqE) = case l of
301 val leEdge = Le (x,y, Thm ([p], Less.less_imp_le))
302 val neqEdges = [ NotEq (x,y, Thm ([p], Less.less_imp_neq)),
303 NotEq (y,x, Thm ( [Thm ([p], Less.less_imp_neq)], thm "not_sym"))]
305 buildGraphs (ls, addEdge(y,[],(addEdge (x,[(y,leEdge)],leG))), neqEdges@neqE)
307 | (Le (x,y,p)) => buildGraphs (ls, addEdge(y,[],(addEdge (x,[(y,l)],leG))), neqE)
308 | _ => buildGraphs (ls, leG, l::neqE) ;
310 in buildGraphs (lessList, [], []) end;
312 (* ********************************************************************** *)
314 (* mkGraph constructs from a list of objects of type less a graph g *)
315 (* Used for plain transitivity chain reasoning. *)
317 (* ********************************************************************** *)
322 fun buildGraph ([],g) = g
323 | buildGraph (l::ls, g) = buildGraph (ls, (addEdge ((lower l),[((upper l),l)],g)))
325 in buildGraph (lessList, []) end;
327 (* *********************************************************************** *)
329 (* adjacent g u : (''a * 'b list ) list -> ''a -> 'b list *)
331 (* List of successors of u in graph g *)
333 (* *********************************************************************** *)
335 fun adjacent eq_comp ((v,adj)::el) u =
336 if eq_comp (u, v) then adj else adjacent eq_comp el u
337 | adjacent _ [] _ = []
339 (* *********************************************************************** *)
341 (* dfs eq_comp g u v: *)
342 (* ('a * 'a -> bool) -> ('a *( 'a * less) list) list -> *)
343 (* 'a -> 'a -> (bool * ('a * less) list) *)
345 (* Depth first search of v from u. *)
346 (* Returns (true, path(u, v)) if successful, otherwise (false, []). *)
348 (* *********************************************************************** *)
350 fun dfs eq_comp g u v =
352 val pred = Unsynchronized.ref [];
353 val visited = Unsynchronized.ref [];
355 fun been_visited v = exists (fn w => eq_comp (w, v)) (!visited)
358 let val _ = visited := u' :: (!visited)
360 fun update (x,l) = let val _ = pred := (x,l) ::(!pred) in () end;
362 in if been_visited v then ()
363 else (app (fn (v',l) => if been_visited v' then () else (
365 dfs_visit v'; ()) )) (adjacent eq_comp g u')
369 if (been_visited v) then (true, (!pred)) else (false , [])
372 (* ************************************************************************ *)
374 (* Begin: Quasi Order relevant functions *)
377 (* ************************************************************************ *)
379 (* ************************************************************************ *)
381 (* findPath x y g: Term.term -> Term.term -> *)
382 (* (Term.term * (Term.term * less list) list) -> *)
383 (* (bool, less list) *)
385 (* Searches a path from vertex x to vertex y in Graph g, returns true and *)
386 (* the list of edges forming the path, if a path is found, otherwise false *)
389 (* ************************************************************************ *)
394 val (found, tmp) = dfs (op aconv) g x y ;
395 val pred = map snd tmp;
399 (* find predecessor u of node v and the edge u -> v *)
400 fun lookup v [] = raise Cannot
401 | lookup v (e::es) = if (upper e) aconv v then e else lookup v es;
403 (* traverse path backwards and return list of visited edges *)
405 let val l = lookup v pred
408 if u aconv x then [l] else (rev_path u) @ [l]
414 if x aconv y then (found,[(Le (x, y, (Thm ([], Less.le_refl))))])
415 else (found, (path x y) ))
420 (* ************************************************************************ *)
422 (* findQuasiProof (leqG, neqE) subgoal: *)
423 (* (Term.term * (Term.term * less list) list) * less list -> less -> proof *)
425 (* Constructs a proof for subgoal by searching a special path in leqG and *)
426 (* neqE. Raises Cannot if construction of the proof fails. *)
428 (* ************************************************************************ *)
431 (* As the conlusion can be either of form x <= y, x < y or x ~= y we have *)
432 (* three cases to deal with. Finding a transitivity path from x to y with label *)
434 (* This is simply done by searching any path from x to y in the graph leG. *)
435 (* The graph leG contains only edges with label <=. *)
438 (* A path from x to y with label < can be found by searching a path with *)
439 (* label <= from x to y in the graph leG and merging the path x <= y with *)
440 (* a parallel edge x ~= y resp. y ~= x to x < y. *)
443 (* If the conclusion is of form x ~= y, we can find a proof either directly, *)
444 (* if x ~= y or y ~= x are among the assumptions, or by constructing x ~= y if *)
445 (* x < y or y < x follows from the assumptions. *)
447 fun findQuasiProof (leG, neqE) subgoal =
448 case subgoal of (Le (x,y, _)) => (
450 val (xyLefound,xyLePath) = findPath x y leG
454 val Le_x_y = (transPath (tl xyLePath, hd xyLePath))
455 in getprf Le_x_y end )
458 | (Less (x,y,_)) => (
460 fun findParallelNeq [] = NONE
461 | findParallelNeq (e::es) =
462 if (x aconv (lower e) andalso y aconv (upper e)) then SOME e
463 else if (y aconv (lower e) andalso x aconv (upper e)) then SOME (NotEq (x,y, (Thm ([getprf e], thm "not_sym"))))
464 else findParallelNeq es ;
466 (* test if there is a edge x ~= y respectivly y ~= x and *)
467 (* if it possible to find a path x <= y in leG, thus we can conclude x < y *)
468 (case findParallelNeq neqE of (SOME e) =>
470 val (xyLeFound,xyLePath) = findPath x y leG
474 val Le_x_y = (transPath (tl xyLePath, hd xyLePath))
475 val Less_x_y = mergeLess (e, Le_x_y)
476 in getprf Less_x_y end
482 (* First check if a single premiss is sufficient *)
483 (case (Library.find_first (fn fact => fact subsumes subgoal) neqE, subgoal) of
484 (SOME (NotEq (x, y, p)), NotEq (x', y', _)) =>
485 if (x aconv x' andalso y aconv y') then p
486 else Thm ([p], thm "not_sym")
491 (* ************************************************************************ *)
493 (* End: Quasi Order relevant functions *)
496 (* ************************************************************************ *)
498 (* *********************************************************************** *)
500 (* solveLeTrans sign (asms,concl) : *)
501 (* theory -> less list * Term.term -> proof list *)
505 (* *********************************************************************** *)
507 fun solveLeTrans thy (asms, concl) =
512 val (subgoal, prf) = mkconcl_trans thy concl
513 val (found, path) = findPath (lower subgoal) (upper subgoal) g
515 if found then [getprf (transPath (tl path, hd path))]
521 (* *********************************************************************** *)
523 (* solveQuasiOrder sign (asms,concl) : *)
524 (* theory -> less list * Term.term -> proof list *)
526 (* Find proof if possible for quasi order. *)
528 (* *********************************************************************** *)
530 fun solveQuasiOrder thy (asms, concl) =
532 val (leG, neqE) = mkQuasiGraph asms
535 val (subgoals, prf) = mkconcl_quasi thy concl
536 fun solve facts less =
537 (case triv_solv less of NONE => findQuasiProof (leG, neqE) less
539 in map (solve asms) subgoals end
542 (* ************************************************************************ *)
547 (* - quasi_tac, solves quasi orders *)
548 (* ************************************************************************ *)
551 (* trans_tac - solves transitivity chains over <= *)
553 fun trans_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
555 val thy = ProofContext.theory_of ctxt;
556 val rfrees = map Free (Term.rename_wrt_term A (Logic.strip_params A));
557 val Hs = map (fn H => subst_bounds (rfrees, H)) (Logic.strip_assums_hyp A);
558 val C = subst_bounds (rfrees, Logic.strip_assums_concl A);
559 val lesss = flat (map_index (mkasm_trans thy o swap) Hs);
560 val prfs = solveLeTrans thy (lesss, C);
562 val (subgoal, prf) = mkconcl_trans thy C;
564 Subgoal.FOCUS (fn {prems, ...} =>
565 let val thms = map (prove prems) prfs
566 in rtac (prove thms prf) 1 end) ctxt n st
568 handle Contr p => Subgoal.FOCUS (fn {prems, ...} => rtac (prove prems p) 1) ctxt n st
569 | Cannot => Seq.empty);
572 (* quasi_tac - solves quasi orders *)
574 fun quasi_tac ctxt = SUBGOAL (fn (A, n) => fn st =>
576 val thy = ProofContext.theory_of ctxt
577 val rfrees = map Free (Term.rename_wrt_term A (Logic.strip_params A));
578 val Hs = map (fn H => subst_bounds (rfrees, H)) (Logic.strip_assums_hyp A);
579 val C = subst_bounds (rfrees, Logic.strip_assums_concl A);
580 val lesss = flat (map_index (mkasm_quasi thy o swap) Hs);
581 val prfs = solveQuasiOrder thy (lesss, C);
582 val (subgoals, prf) = mkconcl_quasi thy C;
584 Subgoal.FOCUS (fn {prems, ...} =>
585 let val thms = map (prove prems) prfs
586 in rtac (prove thms prf) 1 end) ctxt n st
589 (Subgoal.FOCUS (fn {prems, ...} => rtac (prove prems p) 1) ctxt n st
590 handle Subscript => Seq.empty)
591 | Cannot => Seq.empty
592 | Subscript => Seq.empty);