src/Tools/Argo/argo_cc.ML
changeset 63960 3daf02070be5
child 64927 a5a09855e424
equal deleted inserted replaced
63959:f77dca1abf1b 63960:3daf02070be5
       
     1 (*  Title:      Tools/Argo/argo_cc.ML
       
     2     Author:     Sascha Boehme
       
     3     Author:     Dmitriy Traytel and Matthias Franze, TU Muenchen
       
     4 
       
     5 Equality reasoning based on congurence closure. It features:
       
     6 
       
     7   * congruence closure for any term that participates in equalities
       
     8   * support for predicates
       
     9 
       
    10 These features might be added:
       
    11 
       
    12   * caching of explanations while building proofs to obtain shorter proofs
       
    13     and faster proof checking
       
    14   * propagating relevant merges of equivalence classes to all other theory solvers
       
    15   * propagating new relevant negated equalities to all other theory solvers
       
    16   * creating lemma "f ~= g | a ~= b | f a = g b" for asserted negated equalities
       
    17     between "f a" and "g b" (dynamic ackermannization)
       
    18 
       
    19 The implementation is inspired by:
       
    20 
       
    21   Robert Nieuwenhuis and Albert Oliveras. Fast Congruence Closure and
       
    22   Extensions. In Information and Computation, volume 205(4),
       
    23   pages 557-580, 2007.
       
    24 
       
    25   Harald Ganzinger, George Hagen, Robert Nieuwenhuis, Albert Oliveras,
       
    26   Cesare Tinelli. DPLL(T): Fast decision procedures. In Lecture Notes in
       
    27   Computer Science, volume 3114, pages 175-188. Springer, 2004.
       
    28 *)
       
    29 
       
    30 signature ARGO_CC =
       
    31 sig
       
    32   (* context *)
       
    33   type context
       
    34   val context: context
       
    35 
       
    36   (* simplification *)
       
    37   val simplify: Argo_Rewr.context -> Argo_Rewr.context
       
    38   
       
    39   (* enriching the context *)
       
    40   val add_atom: Argo_Term.term -> context -> Argo_Lit.literal option * context
       
    41 
       
    42   (* main operations *)
       
    43   val assume: Argo_Common.literal -> context -> Argo_Lit.literal Argo_Common.implied * context
       
    44   val check: context -> Argo_Lit.literal Argo_Common.implied * context
       
    45   val explain: Argo_Lit.literal -> context -> (Argo_Cls.clause * context) option
       
    46   val add_level: context -> context
       
    47   val backtrack: context -> context
       
    48 end
       
    49 
       
    50 structure Argo_Cc: ARGO_CC =
       
    51 struct
       
    52 
       
    53 (* tables indexed by pairs of terms *)
       
    54 
       
    55 val term2_ord = prod_ord Argo_Term.term_ord Argo_Term.term_ord
       
    56 
       
    57 structure Argo_Term2tab = Table(type key = Argo_Term.term * Argo_Term.term val ord = term2_ord)
       
    58 
       
    59 
       
    60 (* equality certificates *)
       
    61 
       
    62 (*
       
    63   The solver keeps assumed equalities to produce explanations later on.
       
    64 
       
    65   A flat equality (lp, (t1, t2)) consists of the assumed literal and its proof
       
    66   as well as the terms t1 and t2 that are assumed to be equal. The literal expresses
       
    67   the equality t1 = t2.
       
    68 
       
    69   A congruence equality (t1, t2) is an equality t1 = t2 where both terms are
       
    70   applications (f a) and (g b).
       
    71 
       
    72   A symmetric equality eq is a marker for applying the symmetry rule to eq.
       
    73 *)
       
    74 
       
    75 datatype eq =
       
    76   Flat of Argo_Common.literal * (Argo_Term.term * Argo_Term.term) |
       
    77   Cong of Argo_Term.term * Argo_Term.term |
       
    78   Symm of eq
       
    79 
       
    80 fun dest_eq (Flat (_, tp)) = tp
       
    81   | dest_eq (Cong tp) = tp
       
    82   | dest_eq (Symm eq) = swap (dest_eq eq)
       
    83 
       
    84 fun symm (Symm eq) = eq
       
    85   | symm eq = Symm eq
       
    86 
       
    87 fun negate (Flat ((lit, p), tp)) = Flat ((Argo_Lit.negate lit, p), tp)
       
    88   | negate (Cong tp) = Cong tp
       
    89   | negate (Symm eq) = Symm (negate eq)
       
    90 
       
    91 fun dest_app (Argo_Term.T (_, Argo_Expr.App, [t1, t2])) = (t1, t2)
       
    92   | dest_app _ = raise Fail "bad application"
       
    93 
       
    94 
       
    95 (* context *)
       
    96 
       
    97 (*
       
    98   Each representative keeps track of the yet unimplied atoms in which this any member of
       
    99   this representative's equivalence class occurs. An atom is either a list of equalities
       
   100   between two terms, a list of predicates or a certificate. The certificate denotes that
       
   101   this equivalence class contains already implied predicates, and the literal accompanying
       
   102   the certificate specifies the polarity of these predicates.
       
   103 *)
       
   104 
       
   105 datatype atoms =
       
   106   Eqs of (Argo_Term.term * Argo_Term.term) list |
       
   107   Preds of Argo_Term.term list |
       
   108   Cert of Argo_Common.literal
       
   109 
       
   110 (*
       
   111   Each representative has an associated ritem that contains the members of the
       
   112   equivalence class, the yet unimplied atoms and further information.
       
   113 *)
       
   114 
       
   115 type ritem = {
       
   116   size: int, (* the size of the equivalence class *)
       
   117   class: Argo_Term.term list, (* the equivalence class as a list of distinct terms *)
       
   118   occs: Argo_Term.term list, (* a list of all application terms in which members of
       
   119     the equivalence class occur either as function or as argument *)
       
   120   neqs: (Argo_Term.term * eq) list, (* a list of terms from disjoint equivalence classes,
       
   121     for each term of this list there is a certificate of a negated equality that is
       
   122     required to explain why the equivalence classes are disjoint *)
       
   123   atoms: atoms} (* the atoms of the representative *)
       
   124 
       
   125 type repr = Argo_Term.term Argo_Termtab.table
       
   126 type rdata = ritem Argo_Termtab.table
       
   127 type apps = Argo_Term.term Argo_Term2tab.table
       
   128 type trace = (Argo_Term.term * eq) Argo_Termtab.table
       
   129 
       
   130 type context = {
       
   131   repr: repr, (* a table mapping terms to their representatives *)
       
   132   rdata: rdata, (* a table mapping representatives to their ritems *)
       
   133   apps: apps, (* a table mapping a function and an argument to their application *)
       
   134   trace: trace, (* the proof forest used to trace assumed and implied equalities *)
       
   135   prf: Argo_Proof.context, (* the proof context *)
       
   136   back: (repr * rdata * apps * trace) list} (* backtracking information *)
       
   137 
       
   138 fun mk_context repr rdata apps trace prf back: context =
       
   139   {repr=repr, rdata=rdata, apps=apps, trace=trace, prf=prf, back=back}
       
   140 
       
   141 val context =
       
   142   mk_context Argo_Termtab.empty Argo_Termtab.empty Argo_Term2tab.empty Argo_Termtab.empty
       
   143     Argo_Proof.cc_context []
       
   144 
       
   145 fun repr_of repr t = the_default t (Argo_Termtab.lookup repr t)
       
   146 fun repr_of' ({repr, ...}: context) = repr_of repr
       
   147 fun put_repr t r = Argo_Termtab.update (t, r)
       
   148 
       
   149 fun mk_ritem size class occs neqs atoms: ritem =
       
   150   {size=size, class=class, occs=occs, neqs=neqs, atoms=atoms}
       
   151 
       
   152 fun as_ritem t = mk_ritem 1 [t] [] [] (Eqs [])
       
   153 fun as_pred_ritem t = mk_ritem 1 [t] [] [] (Preds [t])
       
   154 fun gen_ritem_of mk rdata r = the_default (mk r) (Argo_Termtab.lookup rdata r)
       
   155 fun ritem_of rdata = gen_ritem_of as_ritem rdata
       
   156 fun ritem_of_pred rdata = gen_ritem_of as_pred_ritem rdata
       
   157 fun ritem_of' ({rdata, ...}: context) = ritem_of rdata
       
   158 fun put_ritem r ri = Argo_Termtab.update (r, ri)
       
   159 
       
   160 fun add_occ r occ = Argo_Termtab.map_default (r, as_ritem r)
       
   161   (fn {size, class, occs, neqs, atoms}: ritem => mk_ritem size class (occ :: occs) neqs atoms)
       
   162 
       
   163 fun put_atoms atoms ({size, class, occs, neqs, ...}: ritem) = mk_ritem size class occs neqs atoms
       
   164 
       
   165 fun add_eq_atom r atom = Argo_Termtab.map_default (r, as_ritem r)
       
   166   (fn ri as {atoms=Eqs atoms, ...}: ritem => put_atoms (Eqs (atom :: atoms)) ri
       
   167     | ri => put_atoms (Eqs [atom]) ri)
       
   168 
       
   169 fun lookup_app apps tp = Argo_Term2tab.lookup apps tp
       
   170 fun put_app tp app = Argo_Term2tab.update_new (tp, app)
       
   171 
       
   172 
       
   173 (* traces for explanations *)
       
   174 
       
   175 (*
       
   176   Assumed and implied equalities are collected in a proof forest for being able to
       
   177   produce explanations. For each equivalence class there is one proof tree. The
       
   178   equality certificates are oriented towards a root term, that is not necessarily
       
   179   the representative of the equivalence class.
       
   180 *)
       
   181 
       
   182 (*
       
   183   Whenever two equivalence classes are merged due to an equality t1 = t2, the shorter
       
   184   of the two paths, either from t1 to its root or t2 to its root, is re-oriented such
       
   185   that the relevant ti becomes the new root of its tree. Then, a new edge between ti
       
   186   and the other term of the equality t1 = t2 is added to connect the two proof trees.
       
   187 *)
       
   188 
       
   189 fun depth_of trace t =
       
   190   (case Argo_Termtab.lookup trace t of
       
   191     NONE => 0
       
   192   | SOME (t', _) => 1 + depth_of trace t')
       
   193 
       
   194 fun reorient t trace =
       
   195   (case Argo_Termtab.lookup trace t of
       
   196     NONE => trace
       
   197   | SOME (t', eq) => Argo_Termtab.update (t', (t, symm eq)) (reorient t' trace))
       
   198 
       
   199 fun new_edge from to eq trace = Argo_Termtab.update (from, (to, eq)) (reorient from trace)
       
   200 
       
   201 fun with_shortest f (t1, t2) eq trace =
       
   202   (if depth_of trace t1 <= depth_of trace t2 then f t1 t2 eq else f t2 t1 (symm eq)) trace
       
   203 
       
   204 fun add_edge eq trace = with_shortest new_edge (dest_eq eq) eq trace
       
   205 
       
   206 (*
       
   207   To produce an explanation that t1 and t2 are equal, the paths to their root are
       
   208   extracted from the proof forest. Common ancestors in both paths are dropped.
       
   209 *)
       
   210 
       
   211 fun path_to_root trace path t =
       
   212   (case Argo_Termtab.lookup trace t of
       
   213     NONE => (t, path)
       
   214   | SOME (t', _) => path_to_root trace (t :: path) t')
       
   215 
       
   216 fun drop_common root (t1 :: path1) (t2 :: path2) =
       
   217       if Argo_Term.eq_term (t1, t2) then drop_common t1 path1 path2 else root
       
   218   | drop_common root _ _ = root
       
   219 
       
   220 fun common_ancestor trace t1 t2 =
       
   221   let val ((root, path1), (_, path2)) = apply2 (path_to_root trace []) (t1, t2)
       
   222   in drop_common root path1 path2 end
       
   223 
       
   224 (*
       
   225   The proof of an assumed literal is typically a hypothesis. If the assumed literal is
       
   226   already known to be a unit literal, then there is already a proof for it.
       
   227 *)
       
   228 
       
   229 fun proof_of (lit, NONE) lits prf =
       
   230       (insert Argo_Lit.eq_lit (Argo_Lit.negate lit) lits, Argo_Proof.mk_hyp lit prf)
       
   231   | proof_of (_, SOME p) lits prf = (lits, (p, prf))
       
   232 
       
   233 (*
       
   234   The explanation of equality between two terms t1 and t2 is computed based on the
       
   235   paths from t1 and t2 to their common ancestor t in the proof forest. For each of
       
   236   the two paths, a transitive proof of equality t1 = t and t = t2 is constructed,
       
   237   such that t1 = t2 follows by transitivity.
       
   238   
       
   239   Each edge of the paths denotes an assumed or implied equality. Implied equalities
       
   240   might be due to congruences (f a = g b) for which the equalities f = g and a = b
       
   241   need to be explained recursively.
       
   242 *)
       
   243 
       
   244 fun mk_eq_proof trace t1 t2 lits prf =
       
   245   if Argo_Term.eq_term (t1, t2) then (lits, Argo_Proof.mk_refl t1 prf)
       
   246   else
       
   247     let
       
   248       val root = common_ancestor trace t1 t2
       
   249       val (lits, (p1, prf)) = trans_proof I I trace t1 root lits prf
       
   250       val (lits, (p2, prf)) = trans_proof swap symm trace t2 root lits prf
       
   251     in (lits, Argo_Proof.mk_trans p1 p2 prf) end
       
   252 
       
   253 and trans_proof sw sy trace t root lits prf =
       
   254   if Argo_Term.eq_term (t, root) then (lits, Argo_Proof.mk_refl t prf)
       
   255   else
       
   256     (case Argo_Termtab.lookup trace t of
       
   257       NONE => raise Fail "bad trace"
       
   258     | SOME (t', eq) => 
       
   259         let
       
   260           val (lits, (p1, prf)) = proof_step trace (sy eq) lits prf
       
   261           val (lits, (p2, prf)) = trans_proof sw sy trace t' root lits prf
       
   262         in (lits, uncurry Argo_Proof.mk_trans (sw (p1, p2)) prf) end)
       
   263 
       
   264 and proof_step _ (Flat (cert, _)) lits prf = proof_of cert lits prf
       
   265   | proof_step trace (Cong tp) lits prf =
       
   266       let
       
   267         val ((t1, t2), (u1, u2)) = apply2 dest_app tp
       
   268         val (lits, (p1, prf)) = mk_eq_proof trace t1 u1 lits prf
       
   269         val (lits, (p2, prf)) = mk_eq_proof trace t2 u2 lits prf
       
   270       in (lits, Argo_Proof.mk_cong p1 p2 prf) end
       
   271   | proof_step trace (Symm eq) lits prf =
       
   272       proof_step trace eq lits prf ||> uncurry Argo_Proof.mk_symm
       
   273 
       
   274 (*
       
   275   All clauses produced by a theory solver are expected to be a lemma.
       
   276   The lemma proof must hence be the last proof step.
       
   277 *)
       
   278 
       
   279 fun close_proof lit lits (p, prf) = (lit :: lits, Argo_Proof.mk_lemma [lit] p prf)
       
   280 
       
   281 (*
       
   282   The explanation for the equality of t1 and t2 used the above algorithm.
       
   283 *)
       
   284 
       
   285 fun explain_eq lit t1 t2 ({repr, rdata, apps, trace, prf, back}: context) =
       
   286   let val (lits, (p, prf)) = mk_eq_proof trace t1 t2 [] prf |-> close_proof lit
       
   287   in ((lits, p), mk_context repr rdata apps trace prf back) end
       
   288 
       
   289 (*
       
   290   The explanation that t1 and t2 are distinct uses the negated equality u1 ~= u2 that
       
   291   explains why the equivalence class containing t1 and u1 and the equivalence class
       
   292   containing t2 and u2 are disjoint. The explanations for t1 = u1 and u2 = t2 are
       
   293   constructed using the above algorithm. By transitivity, it follows that t1 ~= t2.  
       
   294 *)
       
   295 
       
   296 fun finish_proof (Flat ((lit, _), _)) lits p prf = close_proof lit lits (p, prf)
       
   297   | finish_proof (Cong _) _ _ _ = raise Fail "bad equality"
       
   298   | finish_proof (Symm eq) lits p prf = Argo_Proof.mk_symm p prf |-> finish_proof eq lits
       
   299 
       
   300 fun explain_neq eq eq' ({repr, rdata, apps, trace, prf, back}: context) =
       
   301   let
       
   302     val (t1, t2) = dest_eq eq
       
   303     val (u1, u2) = dest_eq eq'
       
   304 
       
   305     val (lits, (p, prf)) = proof_step trace eq' [] prf
       
   306     val (lits, (p1, prf)) = mk_eq_proof trace t1 u1 lits prf
       
   307     val (lits, (p2, prf)) = mk_eq_proof trace u2 t2 lits prf
       
   308     val (lits, (p, prf)) = 
       
   309       Argo_Proof.mk_trans p p2 prf |-> Argo_Proof.mk_trans p1 |-> finish_proof eq lits
       
   310   in ((lits, p), mk_context repr rdata apps trace prf back) end
       
   311 
       
   312 
       
   313 (* propagating new equalities *)
       
   314 
       
   315 exception CONFLICT of Argo_Cls.clause * context
       
   316 
       
   317 (*
       
   318   comment missing
       
   319 *)
       
   320 
       
   321 fun same_repr repr r (t, _) = Argo_Term.eq_term (r, repr_of repr t)
       
   322 
       
   323 fun has_atom rdata r eq =
       
   324   (case #atoms (ritem_of rdata r) of
       
   325     Eqs eqs => member (Argo_Term.eq_term o snd) eqs eq
       
   326   | _ => false)
       
   327 
       
   328 fun add_implied mk_lit repr rdata r neqs (atom as (t, eq)) (eqs, ls) =
       
   329   let val r' = repr_of repr t
       
   330   in
       
   331     if Argo_Term.eq_term (r, r') then (eqs, insert Argo_Lit.eq_lit (mk_lit eq) ls)
       
   332     else if exists (same_repr repr r') neqs andalso has_atom rdata r' eq then
       
   333       (eqs, Argo_Lit.Neg eq :: ls)
       
   334     else (atom :: eqs, ls)
       
   335   end
       
   336 
       
   337 (*
       
   338   comment missing
       
   339 *)
       
   340 
       
   341 fun copy_occ repr app (eqs, occs, apps) =
       
   342   let val rp = apply2 (repr_of repr) (dest_app app)
       
   343   in
       
   344     (case lookup_app apps rp of
       
   345       SOME app' => (Cong (app, app') :: eqs, occs, apps)
       
   346     | NONE => (eqs, app :: occs, put_app rp app apps))
       
   347   end
       
   348 
       
   349 (*
       
   350   comment missing
       
   351 *)
       
   352 
       
   353 fun add_lits (Argo_Lit.Pos _, _) = fold (cons o Argo_Lit.Pos)
       
   354   | add_lits (Argo_Lit.Neg _, _) = fold (cons o Argo_Lit.Neg)
       
   355 
       
   356 fun join_atoms f (Eqs eqs1) (Eqs eqs2) ls = f eqs1 eqs2 ls
       
   357   | join_atoms _ (Preds ts1) (Preds ts2) ls = (Preds (union Argo_Term.eq_term ts1 ts2), ls)
       
   358   | join_atoms _ (Preds ts) (Cert lp) ls = (Cert lp, add_lits lp ts ls)
       
   359   | join_atoms _ (Cert lp) (Preds ts) ls = (Cert lp, add_lits lp ts ls)
       
   360   | join_atoms _ (Cert lp) (Cert _) ls = (Cert lp, ls)
       
   361   | join_atoms _ _ _ _ = raise Fail "bad atoms"
       
   362 
       
   363 (*
       
   364   comment missing
       
   365 *)
       
   366 
       
   367 fun join r1 ri1 r2 ri2 eq (eqs, ls, {repr, rdata, apps, trace, prf, back}: context) =
       
   368   let
       
   369     val {size=size1, class=class1, occs=occs1, neqs=neqs1, atoms=atoms1}: ritem = ri1
       
   370     val {size=size2, class=class2, occs=occs2, neqs=neqs2, atoms=atoms2}: ritem = ri2
       
   371 
       
   372     val repr = fold (fn t => put_repr t r1) class2 repr
       
   373     val class = fold cons class2 class1
       
   374     val (eqs, occs, apps) = fold (copy_occ repr) occs2 (eqs, occs1, apps)
       
   375     val trace = add_edge eq trace
       
   376     val neqs = AList.merge Argo_Term.eq_term (K true) (neqs1, neqs2)
       
   377     fun add r neqs = fold (add_implied Argo_Lit.Pos repr rdata r neqs)
       
   378     fun adds eqs1 eqs2 ls = ([], ls) |> add r2 neqs2 eqs1 |> add r1 neqs1 eqs2 |>> Eqs
       
   379     val (atoms, ls) = join_atoms adds atoms1 atoms2 ls
       
   380     (* TODO: make sure that all implied literals are propagated *)
       
   381     val rdata = put_ritem r1 (mk_ritem (size1 + size2) class occs neqs atoms) rdata
       
   382   in (eqs, ls, mk_context repr rdata apps trace prf back) end
       
   383 
       
   384 (*
       
   385   comment missing
       
   386 *)
       
   387 
       
   388 fun find_neq ({repr, ...}: context) ({neqs, ...}: ritem) r = find_first (same_repr repr r) neqs
       
   389 
       
   390 fun check_join (r1, r2) (ri1, ri2) eq (ecx as (_, _, cx)) =
       
   391   (case find_neq cx ri2 r1 of
       
   392     SOME (_, eq') => raise CONFLICT (explain_neq (negate (symm eq)) eq' cx)
       
   393   | NONE =>
       
   394       (case find_neq cx ri1 r2 of
       
   395         SOME (_, eq') => raise CONFLICT (explain_neq (negate eq) eq' cx)
       
   396       | NONE => join r1 ri1 r2 ri2 eq ecx))
       
   397 
       
   398 (*
       
   399   comment missing
       
   400 *)
       
   401 
       
   402 fun with_max_class f (rp as (r1, r2)) (rip as (ri1: ritem, ri2: ritem)) eq =
       
   403   if #size ri1 >= #size ri2 then f rp rip eq else f (r2, r1) (ri2, ri1) (symm eq)
       
   404 
       
   405 (*
       
   406   comment missing
       
   407 *)
       
   408 
       
   409 fun propagate ([], ls, cx) = (rev ls, cx)
       
   410   | propagate (eq :: eqs, ls, cx) =
       
   411       let val rp = apply2 (repr_of' cx) (dest_eq eq)
       
   412       in 
       
   413         if Argo_Term.eq_term rp then propagate (eqs, ls, cx)
       
   414         else propagate (with_max_class check_join rp (apply2 (ritem_of' cx) rp) eq (eqs, ls, cx))
       
   415       end
       
   416 
       
   417 fun without lit (lits, cx) = (Argo_Common.Implied (remove Argo_Lit.eq_lit lit lits), cx)
       
   418 
       
   419 fun flat_merge (lp as (lit, _)) eq cx = without lit (propagate ([Flat (lp, eq)], [], cx))
       
   420   handle CONFLICT (cls, cx) => (Argo_Common.Conflict cls, cx)
       
   421 
       
   422 (*
       
   423   comment missing
       
   424 *)
       
   425 
       
   426 fun app_merge app tp (cx as {repr, rdata, apps, trace, prf, back}: context) =
       
   427   let val rp as (r1, r2) = apply2 (repr_of repr) tp
       
   428   in
       
   429     (case lookup_app apps rp of
       
   430       SOME app' =>
       
   431         (case propagate ([Cong (app, app')], [], cx) of
       
   432           ([], cx) => cx
       
   433         | _ => raise Fail "bad application merge")
       
   434     | NONE =>
       
   435         let val rdata = add_occ r1 app (add_occ r2 app rdata)
       
   436         in mk_context repr rdata (put_app rp app apps) trace prf back end)
       
   437   end
       
   438 
       
   439 (*
       
   440   A negated equality between t1 and t2 is only recorded if t1 and t2 are not already known
       
   441   to belong to the same class. In that case, a conflict is raised with an explanation
       
   442   why t1 and t2 are equal. Otherwise, the classes of t1 and t2 are marked as disjoint by
       
   443   storing the negated equality in the ritems of t1's and t2's representative. All equalities
       
   444   between terms of t1's and t2's class are implied as negated equalities. Those equalities
       
   445   are found in the ritems of t1's and t2's representative.
       
   446 *)
       
   447 
       
   448 fun note_neq eq (r1, r2) (t1, t2) ({repr, rdata, apps, trace, prf, back}: context) =
       
   449   let
       
   450     val {size=size1, class=class1, occs=occs1, neqs=neqs1, atoms=atoms1}: ritem = ritem_of rdata r1
       
   451     val {size=size2, class=class2, occs=occs2, neqs=neqs2, atoms=atoms2}: ritem = ritem_of rdata r2
       
   452 
       
   453     fun add r (Eqs eqs) ls = fold (add_implied Argo_Lit.Neg repr rdata r []) eqs ([], ls) |>> Eqs
       
   454       | add _ _ _ = raise Fail "bad negated equality between predicates"
       
   455     val ((atoms1, atoms2), ls) = [] |> add r2 atoms1 ||>> add r1 atoms2
       
   456     val ri1 = mk_ritem size1 class1 occs1 ((t2, eq) :: neqs1) atoms1
       
   457     val ri2 = mk_ritem size2 class2 occs2 ((t1, symm eq) :: neqs2) atoms2
       
   458   in (ls, mk_context repr (put_ritem r1 ri1 (put_ritem r2 ri2 rdata)) apps trace prf back) end
       
   459 
       
   460 fun flat_neq (lp as (lit, _)) (tp as (t1, t2)) cx =
       
   461   let val rp = apply2 (repr_of' cx) tp
       
   462   in
       
   463     if Argo_Term.eq_term rp then
       
   464       let val (cls, cx) = explain_eq (Argo_Lit.negate lit) t1 t2 cx
       
   465       in (Argo_Common.Conflict cls, cx) end
       
   466     else without lit (note_neq (Flat (lp, tp)) rp tp cx)
       
   467   end
       
   468 
       
   469 
       
   470 (* simplification *)
       
   471 
       
   472 (*
       
   473   Only equalities are subject to normalizations. An equality between two expressions e1 and e2
       
   474   is normalized, if e1 is less than e2 based on the expression ordering. If e1 and e2 are
       
   475   syntactically equal, the equality between these two expressions is normalized to the true
       
   476   expression.
       
   477 *)
       
   478 
       
   479 fun norm_eq env =
       
   480   let val e1 = Argo_Rewr.get env 1 and e2 = Argo_Rewr.get env 2
       
   481   in
       
   482     (case Argo_Expr.expr_ord (e1, e2) of
       
   483       EQUAL => SOME (Argo_Proof.Rewr_Eq_Refl, Argo_Rewr.E Argo_Expr.true_expr)
       
   484     | LESS => NONE
       
   485     | GREATER => SOME (Argo_Proof.Rewr_Eq_Symm, Argo_Rewr.E (Argo_Expr.mk_eq e2 e1)))
       
   486   end
       
   487 
       
   488 val simplify = Argo_Rewr.func "(eq (? 1) (? 2))" norm_eq
       
   489 
       
   490 
       
   491 (* declaring atoms *)
       
   492 
       
   493 (*
       
   494   Only a genuinely new equality term t for the equality "t1 = t2" is added. If t1 and t2 belong
       
   495   to the same equality class or if the classes of t1 and t2 are known to be disjoint, the
       
   496   respective literal is returned together with an unmodified context.
       
   497 *)
       
   498 
       
   499 fun add_eq_term t t1 t2 (rp as (r1, r2)) (cx as {repr, rdata, apps, trace, prf, back}: context) =
       
   500   if Argo_Term.eq_term rp then (SOME (Argo_Lit.Pos t), cx)
       
   501   else if is_some (find_neq cx (ritem_of rdata r1) r2) then (SOME (Argo_Lit.Neg t), cx)
       
   502   else
       
   503     let val rdata = add_eq_atom r1 (t2, t) (add_eq_atom r2 (t1, t) rdata)
       
   504     in (NONE, mk_context repr rdata apps trace prf back) end
       
   505 
       
   506 (*
       
   507   Only a genuinely new predicate t, which is an application "t1 t2", is added.
       
   508   If there is a predicate that is known to be congruent to the representatives of t1 and t2,
       
   509   and that predicate or its negation has already been assummed, the respective literal of t
       
   510   is returned together with an unmodified context.
       
   511 *)
       
   512 
       
   513 fun add_pred_term t rp (cx as {repr, rdata, apps, trace, prf, back}: context) =
       
   514   (case lookup_app apps rp of
       
   515     NONE => (NONE, mk_context repr (put_ritem t (as_pred_ritem t) rdata) apps trace prf back)
       
   516   | SOME app =>
       
   517       (case `(ritem_of_pred rdata) (repr_of repr app) of
       
   518         ({atoms=Cert (Argo_Lit.Pos _, _), ...}: ritem, _) => (SOME (Argo_Lit.Pos t), cx)
       
   519       | ({atoms=Cert (Argo_Lit.Neg _, _), ...}: ritem, _) => (SOME (Argo_Lit.Neg t), cx)
       
   520       | (ri as {atoms=Preds ts, ...}: ritem, r) =>
       
   521           let val rdata = put_ritem r (put_atoms (Preds (t :: ts)) ri) rdata
       
   522           in (NONE, mk_context repr rdata apps trace prf back) end
       
   523       | ({atoms=Eqs _, ...}: ritem, _) => raise Fail "bad predicate"))
       
   524 
       
   525 (*
       
   526   For each term t that is an application "t1 t2", the reflexive equality t = t1 t2 is added
       
   527   to the context. This is required for propagations of congruences.
       
   528 *)
       
   529 
       
   530 fun flatten (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])) cx =
       
   531       flatten t1 (flatten t2 (app_merge t (t1, t2) cx))
       
   532   | flatten _ cx = cx
       
   533 
       
   534 (*
       
   535   Atoms to be added to the context must either be an equality "t1 = t2" or
       
   536   an application "t1 t2" (a predicate). Besides adding the equality or the application,
       
   537   reflexive equalities for for all applications in the terms t1 and t2 are added.
       
   538 *)
       
   539 
       
   540 fun add_atom (t as Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])) cx =
       
   541       add_eq_term t t1 t2 (apply2 (repr_of' cx) (t1, t2)) (flatten t1 (flatten t2 cx))
       
   542   | add_atom (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])) cx =
       
   543       let val cx = flatten t1 (flatten t2 (app_merge t (t1, t2) cx))
       
   544       in add_pred_term t (apply2 (repr_of' cx) (t1, t2)) cx end
       
   545   | add_atom _ cx = (NONE, cx)
       
   546 
       
   547 
       
   548 (* assuming external knowledge *)
       
   549 
       
   550 (*
       
   551   Assuming a predicate r replaces all predicate atoms of r's ritem with the assumed certificate.
       
   552   The predicate atoms are implied, either with positive or with negative polarity based on
       
   553   the assumption.
       
   554 
       
   555   There must not be a certificate for r since otherwise r would have been assumed before already.
       
   556 *)
       
   557 
       
   558 fun assume_pred lit mk_lit cert r ({repr, rdata, apps, trace, prf, back}: context) =
       
   559   (case ritem_of_pred rdata r of
       
   560     {size, class, occs, neqs, atoms=Preds ts}: ritem =>
       
   561       let val rdata = put_ritem r (mk_ritem size class occs neqs cert) rdata
       
   562       in without lit (map mk_lit ts, mk_context repr rdata apps trace prf back) end
       
   563   | _ => raise Fail "bad predicate assumption")
       
   564 
       
   565 (*
       
   566   Assumed equalities "t1 = t2" are treated as flat equalities between terms t1 and t2.
       
   567   If t1 and t2 are applications, congruences are propagated as part of the merge between t1 and t2.
       
   568   Negated equalities are handled likewise.
       
   569 
       
   570   Assumed predicates do not trigger congruences. Only predicates of the same class are implied.
       
   571 *)
       
   572 
       
   573 fun assume (lp as (Argo_Lit.Pos (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])), _)) cx =
       
   574       flat_merge lp (t1, t2) cx
       
   575   | assume (lp as (Argo_Lit.Neg (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2])), _)) cx =
       
   576       flat_neq lp (t1, t2) cx
       
   577   | assume (lp as (lit as Argo_Lit.Pos (t as Argo_Term.T (_, Argo_Expr.App, [_, _])), _)) cx =
       
   578       assume_pred lit Argo_Lit.Pos (Cert lp) (repr_of' cx t) cx
       
   579   | assume (lp as (lit as Argo_Lit.Neg (t as Argo_Term.T (_, Argo_Expr.App, [_, _])), _)) cx =
       
   580       assume_pred lit Argo_Lit.Neg (Cert lp) (repr_of' cx t) cx
       
   581   | assume _ cx = (Argo_Common.Implied [], cx)
       
   582 
       
   583 
       
   584 (* checking for consistency and pending implications *)
       
   585 
       
   586 (*
       
   587   The internal model is always kept consistent. All implications are propagated as soon as
       
   588   new information is assumed. Hence, there is nothing to be done here.
       
   589 *)
       
   590 
       
   591 fun check cx = (Argo_Common.Implied [], cx)
       
   592 
       
   593 
       
   594 (* explanations *)
       
   595 
       
   596 (*
       
   597   The explanation for the predicate t, which is an application of t1 and t2, is constructed
       
   598   from the explanation of the predicate application "u1 u2" as well as the equalities "u1 = t1"
       
   599   and "u2 = t2" which both are constructed from the proof forest. The substitution rule is
       
   600   the proof step that concludes "t1 t2" from "u1 u2" and the two equalities "u1 = t1"
       
   601   and "u2 = t2".
       
   602 
       
   603   The atoms part of the ritem of t's representative must be a certificate of an already
       
   604   assumed predicate for otherwise there would be no explanation for t.
       
   605 *)
       
   606 
       
   607 fun explain_pred lit t t1 t2 ({repr, rdata, apps, trace, prf, back}: context) =
       
   608   (case ritem_of_pred rdata (repr_of repr t) of
       
   609     {atoms=Cert (cert as (lit', _)), ...}: ritem =>
       
   610       let
       
   611         val (u1, u2) = dest_app (Argo_Lit.term_of lit')
       
   612         val (lits, (p, prf)) = proof_of cert [] prf
       
   613         val (lits, (p1, prf)) = mk_eq_proof trace u1 t1 lits prf
       
   614         val (lits, (p2, prf)) = mk_eq_proof trace u2 t2 lits prf
       
   615         val (lits, (p, prf)) = Argo_Proof.mk_subst p p1 p2 prf |> close_proof lit lits
       
   616       in ((lits, p), mk_context repr rdata apps trace prf back) end
       
   617   | _ => raise Fail "no explanation for bad predicate")
       
   618 
       
   619 (*
       
   620   Explanations are produced based on the proof forest that is constructed while assuming new
       
   621   information and propagating this among the internal data structures.
       
   622   
       
   623   For predicates, no distinction between both polarities needs to be done here. The atoms
       
   624   part of the relevant ritem knows the assumed polarity.
       
   625 *)
       
   626 
       
   627 fun explain (lit as Argo_Lit.Pos (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2]))) cx =
       
   628       SOME (explain_eq lit t1 t2 cx)
       
   629   | explain (lit as Argo_Lit.Neg (Argo_Term.T (_, Argo_Expr.Eq, [t1, t2]))) cx =
       
   630       let val (_, eq) = the (find_neq cx (ritem_of' cx (repr_of' cx t1)) (repr_of' cx t2))
       
   631       in SOME (explain_neq (Flat ((lit, NONE), (t1, t2))) eq cx) end
       
   632   | explain (lit as (Argo_Lit.Pos (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])))) cx =
       
   633       SOME (explain_pred lit t t1 t2 cx)
       
   634   | explain (lit as (Argo_Lit.Neg (t as Argo_Term.T (_, Argo_Expr.App, [t1, t2])))) cx =
       
   635       SOME (explain_pred lit t t1 t2 cx)
       
   636   | explain _ _ = NONE
       
   637 
       
   638 
       
   639 (* backtracking *)
       
   640 
       
   641 (*
       
   642   All information that needs to be reconstructed on backtracking is stored on the backtracking
       
   643   stack. On backtracking any current information is replaced by what was stored before. No copying
       
   644   nor subtle updates are required thanks to immutable data structures.
       
   645 *)
       
   646 
       
   647 fun add_level ({repr, rdata, apps, trace, prf, back}: context) =
       
   648   mk_context repr rdata apps trace prf ((repr, rdata, apps, trace) :: back)
       
   649 
       
   650 fun backtrack ({back=[], ...}: context) = raise Empty
       
   651   | backtrack ({prf, back=(repr, rdata, apps, trace) :: back, ...}: context) =
       
   652       mk_context repr rdata apps trace prf back
       
   653 
       
   654 end