00001 /*****************************************************************************/ 00002 /*! 00003 * \file theory.h 00004 * \brief Generic API for Theories plus methods commonly used by theories 00005 * 00006 * Author: Clark Barrett 00007 * 00008 * Created: Sat Nov 30 23:30:15 2002 00009 * 00010 * <hr> 00011 * Copyright (C) 2003 by the Board of Trustees of Leland Stanford 00012 * Junior University and by New York University. 00013 * 00014 * License to use, copy, modify, sell and/or distribute this software 00015 * and its documentation for any purpose is hereby granted without 00016 * royalty, subject to the terms and conditions defined in the \ref 00017 * LICENSE file provided with this distribution. In particular: 00018 * 00019 * - The above copyright notice and this permission notice must appear 00020 * in all copies of the software and related documentation. 00021 * 00022 * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES, 00023 * EXPRESSED OR IMPLIED. USE IT AT YOUR OWN RISK. 00024 * 00025 * <hr> 00026 * 00027 */ 00028 /*****************************************************************************/ 00029 00030 #ifndef _cvcl__include__theory_h_ 00031 #define _cvcl__include__theory_h_ 00032 00033 #include "expr_stream.h" 00034 #include "common_proof_rules.h" 00035 #include "cdlist.h" 00036 00037 namespace CVCL { 00038 00039 class TheoryCore; 00040 class Theorem; 00041 class Type; 00042 00043 /************************************************************************/ 00044 /*! 00045 *\defgroup Theories Theories 00046 *\ingroup VC 00047 *\brief Theories 00048 *@{ 00049 */ 00050 /***********************************************************************/ 00051 00052 /*****************************************************************************/ 00053 /*! 00054 *\anchor Theory 00055 *\class Theory 00056 *\brief Base class for theories 00057 * 00058 * Author: Clark Barrett 00059 * 00060 * Created: Thu Jan 30 16:37:56 2003 00061 * 00062 * This is an abstract class which all theories should inherit from. In 00063 * addition to providing an abstract theory interface, it provides access 00064 * functions to core functionality. However, in order to avoid duplicating the 00065 * data structures which implement this functionality, all the functionality is 00066 * stored in a separate class (which actually derives from this one) called 00067 * TheoryCore. These two classes work closely together to provide the core 00068 * functionality. 00069 */ 00070 /*****************************************************************************/ 00071 00072 class Theory { 00073 friend class TheoryCore; 00074 private: 00075 ExprManager* d_em; 00076 TheoryCore* d_theoryCore; //!< Provides the core functionality 00077 CommonProofRules* d_commonRules; //!< Commonly used proof rules 00078 std::string d_name; //!< Name of the theory (for debugging) 00079 00080 //! Private default constructor. 00081 /*! Everyone besides TheoryCore has to use the public constructor 00082 which sets up all the provided functionality automatically. 00083 */ 00084 Theory(void); 00085 00086 protected: 00087 bool d_theoryUsed; //! Whether theory has been used (for smtlib translator) 00088 00089 public: 00090 //! Exposed constructor. 00091 /*! Note that each instance of Theory must have a name (mostly for 00092 debugging purposes). */ 00093 Theory(TheoryCore* theoryCore, const std::string& name); 00094 //! Destructor 00095 virtual ~Theory(void); 00096 00097 //! Access to ExprManager 00098 ExprManager* getEM() { return d_em; } 00099 00100 //! Get a pointer to theoryCore 00101 TheoryCore* theoryCore() { return d_theoryCore; } 00102 00103 //! Get a pointer to common proof rules 00104 CommonProofRules* getCommonRules() { return d_commonRules; } 00105 00106 //! Get the name of the theory (for debugging purposes) 00107 const std::string& getName() const { return d_name; } 00108 00109 //! Get whether theory has been used (for smtlib translator) 00110 virtual bool theoryUsed() { return d_theoryUsed; } 00111 00112 /***************************************************************************/ 00113 /*! 00114 *\defgroup Theory_API Abstract Theory Interface 00115 *\anchor theory_api 00116 *\ingroup Theories 00117 *\brief Abstract Theory Interface 00118 * 00119 * These are the theory-specific methods which provide the decision procedure 00120 * functionality for a new theory. At the very least, a theory must 00121 * implement the checkSat method. The other methods can be used to make the 00122 * implementation more convenient. For more information on this API, see 00123 * Clark Barrett's PhD dissertation and \ref theory_api_howto. 00124 *@{ 00125 */ 00126 /***************************************************************************/ 00127 00128 //! Notify theory of a new shared term 00129 /*! When a term e associated with theory i occurs as a child of an expression 00130 associated with theory j, the framework calls i->addSharedTerm(e) and 00131 j->addSharedTerm(e) 00132 */ 00133 virtual void addSharedTerm(const Expr& e) {} 00134 00135 //! Assert a new fact to the decision procedure 00136 /*! Each fact that makes it into the core framework is assigned to exactly 00137 one theory: the theory associated with that fact. assertFact is called to 00138 inform the theory that a new fact has been assigned to the theory. 00139 */ 00140 virtual void assertFact(const Theorem& e) = 0; 00141 00142 //! Check for satisfiability in the theory 00143 /*! \param fullEffort when it is false, checkSat can do as much or 00144 as little work as it likes, though simple inferences and checks for 00145 consistency should be done to increase efficiency. If fullEffort is true, 00146 checkSat must check whether the set of facts given by assertFact together 00147 with the arrangement of shared terms (provided by addSharedTerm) induced by 00148 the global find database equivalence relation are satisfiable. If 00149 satisfiable, checkSat does nothing. 00150 00151 If satisfiability can be acheived by merging some of the shared terms, a new 00152 fact must be enqueued using enqueueFact (this fact need not be a literal). 00153 If there is no way to make things satisfiable, setInconsistent must be called. 00154 */ 00155 virtual void checkSat(bool fullEffort) = 0; 00156 00157 //! Theory-specific rewrite rules. 00158 /*! By default, rewrite just returns a reflexive theorem stating that the 00159 input expression is equivalent to itself. However, rewrite is allowed to 00160 return any theorem which describes how the input expression is equivalent 00161 to some new expression. rewrite should be used to perform simplifications, 00162 normalization, and any other preprocessing on theory-specific expressions 00163 that needs to be done. 00164 */ 00165 virtual Theorem rewrite(const Expr& e) { return reflexivityRule(e); } 00166 00167 //! Set up the term e for call-backs when e or its children change. 00168 /*! setup is called once for each expression associated with the theory. It 00169 is typically used to setup theory-specific data for an expression and to 00170 add call-back information for use with update. 00171 \sa update 00172 */ 00173 virtual void setup(const Expr& e) {} 00174 00175 //! Notify a theory of a new equality 00176 /*! update is a call-back used by the notify mechanism of the core theory. 00177 It works as follows. When an equation t1 = t2 makes it into the core 00178 framework, the two find equivalence classes for t1 and t2 are merged. The 00179 result is that t2 is the new equivalence class representative and t1 is no 00180 longer an equivalence class representative. When this happens, the notify 00181 list of t1 is traversed. Notify list entries consist of a theory and an 00182 expression d. For each entry (i,d), i->update(e, d) is called, where e is 00183 the theorem corresponding to the equality t1=t2. 00184 00185 To add the entry (i,d) to a term t1's notify list, a call must be made to 00186 t1.addNotify(i,d). This is typically done in setup. 00187 00188 \sa setup 00189 */ 00190 virtual void update(const Theorem& e, const Expr& d) {} 00191 00192 //! An optional solver. 00193 /*! The solve method can be used to implement a Shostak-style solver. Since 00194 solvers do not in general combine, the following technique is used. One 00195 theory is designated as the primary solver (in our case, it is the theory 00196 of arithmetic). For each equation that enters the core framework, the 00197 primary solver is called to ensure that the equation is in solved form with 00198 respect to the primary theory. 00199 00200 After the primary solver, the solver for the theory associated with the 00201 equation is called. This solver can do whatever it likes, as long as the 00202 result is still in solved form with respect to the primary solver. This is 00203 a slight generalization of what is described in my (Clark)'s PhD thesis. 00204 */ 00205 virtual Theorem solve(const Theorem& e) { return e; } 00206 00207 ///////////////////////////////// 00208 // Extensions to original API: // 00209 ///////////////////////////////// 00210 00211 //! Recursive simplification step 00212 /*! 00213 * INVARIANT: the result is a Theorem(e=e'), where e' is a fully 00214 * simplified version of e. To simplify subexpressions recursively, 00215 * call simplify() function. 00216 * 00217 * This theory-specific method is called when the simplifier 00218 * descends top-down into the expression. Normally, every kid is 00219 * simplified recursively, and the results are combined into the new 00220 * parent with the same operator (Op). This functionality is 00221 * provided with the default implementation. 00222 * 00223 * However, in some expressions some kids may not matter in the 00224 * result, and can be skipped. For instance, if the first kid in a 00225 * long AND simplifies to FALSE, then the entire expression 00226 * simplifies to FALSE, and the remaining kids do not need to be 00227 * simplified. 00228 * 00229 * This call is a chance for a DP to provide these types of 00230 * optimizations during the top-down phase of simplification. 00231 */ 00232 virtual Theorem simplifyOp(const Expr& e); 00233 00234 //! Check that e is a valid Type expr 00235 virtual void checkType(const Expr& e) 00236 { throw Exception("Cannot construct type from expr: "+e.toString()); } 00237 00238 //! Compute and store the type of e 00239 /*! 00240 * \param e is the expression whose type is computed. 00241 * 00242 * This function computes the type of the top-level operator of e, 00243 * and recurses into children using getType(), if necessary. 00244 */ 00245 virtual void computeType(const Expr& e) {} 00246 //! Compute the base type of the top-level operator of an arbitrary type 00247 virtual Type computeBaseType(const Type& tp) { return tp; } 00248 /*! @brief Theory specific computation of the subtyping predicate for 00249 * type t applied to the expression e. 00250 */ 00251 /*! By default returns true. Each theory needs to compute subtype predicates 00252 * for the types associated with it. So, for example, the theory of records 00253 * will take a record type [# f1: T1, f2: T2 #] and an expression e 00254 * and will return the subtyping predicate for e, namely: 00255 * computeTypePred(T1, e.f1) AND computeTypePred(T2, e.f2) 00256 */ 00257 virtual Expr computeTypePred(const Type& t, const Expr& e) 00258 { return e.getEM()->trueExpr(); } 00259 //! Compute and cache the TCC of e. 00260 /*! 00261 * \param e is an expression (term or formula). This function 00262 * computes the TCC of e which is true iff the expression is defined. 00263 * 00264 * This function computes the TCC or predicate of the top-level 00265 * operator of e, and recurses into children using getTCC(), if 00266 * necessary. 00267 * 00268 * The default implementation is to compute TCCs recursively for all 00269 * children, and return their conjunction. 00270 */ 00271 virtual Expr computeTCC(const Expr& e); 00272 00273 //! Theory-specific parsing implemented by the DP 00274 virtual Expr parseExprOp(const Expr& e) { return e; } 00275 00276 //! Theory-specific pretty-printing. 00277 /*! By default, print the top node in AST, and resume 00278 pretty-printing the children. The same call e.print(os) can be 00279 used in DP-specific printers to use AST printing for the given 00280 node. In fact, it is strongly recommended to add e.print(os) as 00281 the default for all the cases/kinds that are not handled by the 00282 particular pretty-printer. 00283 */ 00284 virtual ExprStream& print(ExprStream& os, const Expr& e) { 00285 return e.printAST(os); 00286 } 00287 00288 //! Add variables from 'e' to 'v' for constructing a concrete model 00289 /*! If e is already of primitive type, do NOT add it to v. */ 00290 virtual void computeModelTerm(const Expr& e, std::vector<Expr>& v); 00291 //! Process disequalities from the arrangement for model generation 00292 virtual void refineCounterExample() {} 00293 //! Assign concrete values to basic-type variables in v 00294 virtual void computeModelBasic(const std::vector<Expr>& v) {} 00295 //! Compute the value of a compound variable from the more primitive ones 00296 /*! The more primitive variables for e are already assigned concrete 00297 * values, and are available through getModelValue(). 00298 * 00299 * The new value for e must be assigned using assignValue() method. 00300 * 00301 * \param e is the compound type expression to assign a value; 00302 * 00303 * \param vars are the variables actually assigned. Normally, 'e' 00304 * is the only element of vars. However, e.g. in the case of 00305 * uninterpreted functions, assigning 'f' means assigning all 00306 * relevant applications of 'f' to constant values (f(0), f(5), 00307 * etc.). Such applications might not be known before the model is 00308 * constructed (they may be of the form f(x), f(y+z), etc., where 00309 * x,y,z are still unassigned). 00310 * 00311 * Populating 'vars' is an opportunity for a DP to change the set of 00312 * top-level "variables" to assign, if needed. In particular, it 00313 * may drop 'e' from the model entirely, if it is already a concrete 00314 * value by itself. 00315 */ 00316 virtual void computeModel(const Expr& e, std::vector<Expr>& vars) { 00317 assignValue(find(e)); 00318 vars.push_back(e); 00319 } 00320 00321 //! Receives all the type predicates for the types of the given theory 00322 /*! Type predicates may be expensive to enqueue eagerly, and DPs may 00323 choose to postpone them, or transform them to something more 00324 efficient. By default, the asserted type predicate is 00325 immediately enqueued as a new fact. 00326 00327 Note: Used only by bitvector theory. 00328 00329 \param e is the expression for which the type predicate is computed 00330 \param pred is the predicate theorem P(e) 00331 */ 00332 virtual void assertTypePred(const Expr& e, const Theorem& pred) 00333 { enqueueFact(pred); } 00334 00335 //! Theory-specific rewrites for atomic formulas 00336 /*! The intended use is to convert complex atomic formulas into an 00337 * equivalent Boolean combination of simpler formulas. Such 00338 * conversion may be harmful for algebraic rewrites, and is not 00339 * always desirable to have in rewrite() method. 00340 * 00341 * Note: Used only by bitvector theory and rewriteLiteral in core. 00342 * 00343 * However, if rewrite() alone cannot solve the problem, and the SAT 00344 * solver needs to be envoked, these additional rewrites may ease 00345 * the job for the SAT solver. 00346 */ 00347 virtual Theorem rewriteAtomic(const Expr& e) { return reflexivityRule(e); } 00348 00349 //! Notification of conflict 00350 /*! 00351 * Decision procedures implement this method when they want to be 00352 * notified about a conflict. 00353 * 00354 * Note: Used only by quantifier theory 00355 * 00356 * \param thm is the theorem of FALSE given to setInconsistent() 00357 */ 00358 virtual void notifyInconsistent(const Theorem& thm) { } 00359 00360 /*@}*/ // End of Theory_API group 00361 00362 /***************************************************************************/ 00363 /*! 00364 *\name Core Framework Functionality 00365 * These methods provide convenient access to core functionality for the 00366 * benefit of decision procedures. 00367 *@{ 00368 */ 00369 /***************************************************************************/ 00370 00371 //! Check if the current context is inconsistent 00372 virtual bool inconsistent(); 00373 //! Make the context inconsistent; The formula proved by e must FALSE. 00374 virtual void setInconsistent(const Theorem& e); 00375 00376 //! Mark the current decision branch as possibly incomplete 00377 /*! 00378 * This should be set when a decision procedure uses an incomplete 00379 * algorithm, and cannot guarantee satisfiability after the final 00380 * checkSat() call with full effort. An example would be 00381 * instantiation of universal quantifiers. 00382 * 00383 * A decision procedure can provide a reason for incompleteness, 00384 * which will be reported back to the user. 00385 */ 00386 virtual void setIncomplete(const std::string& reason); 00387 00388 //! Simplify a term e and return a Theorem(e==e') 00389 /*! \sa simplifyExpr() */ 00390 virtual Theorem simplify(const Expr& e, bool forceRebuild = true); 00391 //! Simplify a term e w.r.t. the current context 00392 /*! \sa simplify */ 00393 Expr simplifyExpr(const Expr& e, bool forceRebuild = true) 00394 { return simplify(e, forceRebuild).getRHS(); } 00395 //! Recursive call of the simplifier. Should only be called from simplifyOp(). 00396 virtual Theorem simplifyRec(const Expr& e); 00397 00398 //! Submit a derived fact to the core from a decision procedure 00399 /*! \param e is the Theorem for the new fact 00400 */ 00401 virtual void enqueueFact(const Theorem& e); 00402 //! Enqueue a new equality e: e1==e2, where e2 is i-leaf simplified 00403 virtual void enqueueEquality(const Theorem& e); 00404 00405 //! Handle new equalities (usually asserted through addFact) 00406 /*! 00407 * INVARIANT: the Theorem 'e' is an equality e1==e2, where e2 is 00408 * i-leaf simplified in the current context, or a conjunction of 00409 * such equalities. 00410 * 00411 * IMPORTANT: decision procedures should NOT use this function in 00412 * update(); use enqueueEquality() instead. 00413 */ 00414 virtual void assertEqualities(const Theorem& e); 00415 00416 //! Parse the generic expression. 00417 /*! This method should be used in parseExprOp() for recursive calls 00418 * to subexpressions, and is the method called by the command 00419 * processor. 00420 */ 00421 virtual Expr parseExpr(const Expr& e); 00422 00423 //! Assigns t a concrete value val. Used in model generation. 00424 virtual void assignValue(const Expr& t, const Expr& val); 00425 //! Record a derived assignment to a variable (LHS). 00426 virtual void assignValue(const Theorem& thm); 00427 00428 /*@}*/ // End of Core Framework Functionality 00429 00430 /***************************************************************************/ 00431 /*! 00432 *\name Theory Helper Methods 00433 * These methods provide basic functionality needed by all theories. 00434 *@{ 00435 */ 00436 /***************************************************************************/ 00437 00438 //! Register new kinds with the given theory 00439 void registerKinds(Theory* theory, std::vector<int>& kinds); 00440 //! Register a new theory 00441 void registerTheory(Theory* theory, std::vector<int>& kinds, 00442 bool hasSolver=false); 00443 00444 //! Return the number of registered theories 00445 int getNumTheories(); 00446 00447 //! Test whether a kind maps to any theory 00448 bool hasTheory(int kind); 00449 //! Return the theory associated with a kind 00450 Theory* theoryOf(int kind); 00451 //! Return the theory associated with an Expr 00452 Theory* theoryOf(const Expr& e); 00453 00454 //! Return the theorem that e is equal to its find 00455 Theorem find(const Expr& e); 00456 //! Return the find of e, or e if it has no find 00457 inline const Expr& findExpr(const Expr& e) 00458 { return e.hasFind() ? find(e).getRHS() : e; } 00459 00460 //! Compute the TCC of e, or the subtyping predicate, if e is a type 00461 Expr getTCC(const Expr& e); 00462 //! Compute (or look up in cache) the base type of e and return the result 00463 Type getBaseType(const Expr& e); 00464 //! Compute the base type from an arbitrary type 00465 Type getBaseType(const Type& tp); 00466 //! Calls the correct theory to compute a type predicate 00467 Expr getTypePred(const Type& t, const Expr& e); 00468 00469 //! Update the children of the term e 00470 /*! When a decision procedure receives a call to update() because a 00471 child of a term 'e' has changed, this method can be called to 00472 compute the new value of 'e'. 00473 \sa update 00474 */ 00475 Theorem updateHelper(const Expr& e); 00476 //! Setup a term for congruence closure (must have sig and rep attributes) 00477 void setupCC(const Expr& e); 00478 //! Update a term w.r.t. congruence closure (must be setup with setupCC()) 00479 void updateCC(const Theorem& e, const Expr& d); 00480 //! Rewrite a term w.r.t. congruence closure (must be setup with setupCC()) 00481 Theorem rewriteCC(const Expr& e); 00482 00483 /*! @brief Calls the correct theory to get all of the terms that 00484 need to be assigned values in the concrete model */ 00485 void getModelTerm(const Expr& e, std::vector<Expr>& v); 00486 //! Fetch the concrete assignment to the variable during model generation 00487 Theorem getModelValue(const Expr& e); 00488 00489 //! Suggest a splitter to the SearchEngine 00490 void addSplitter(const Expr& e, int priority = 0); 00491 00492 /*@}*/ // End of Theory Helper Methods 00493 00494 /***************************************************************************/ 00495 /*! 00496 *\name Core Testers 00497 *@{ 00498 */ 00499 /***************************************************************************/ 00500 00501 //! Test if e is an i-leaf term for the current theory 00502 /*! A term 'e' is an i-leaf for a theory 'i', if it is a variable, 00503 or 'e' belongs to a different theory. This definition makes sense 00504 for a larger term which by itself belongs to the current theory 00505 'i', but (some of) its children are variables or belong to 00506 different theories. */ 00507 bool isLeaf(const Expr& e) { return e.isVar() || theoryOf(e) != this; } 00508 00509 //! Test if e1 is an i-leaf in e2 00510 /*! \sa isLeaf */ 00511 bool isLeafIn(const Expr& e1, const Expr& e2); 00512 00513 //! Test if all i-leaves of e are simplified 00514 /*! \sa isLeaf */ 00515 bool leavesAreSimp(const Expr& e); 00516 00517 /*@}*/ // End of Core Testers 00518 00519 /***************************************************************************/ 00520 /*! 00521 *\name Common Type and Expr Methods 00522 *@{ 00523 */ 00524 /***************************************************************************/ 00525 00526 //! Return BOOLEAN type 00527 Type boolType() { return Type::typeBool(d_em); } 00528 00529 //! Return FALSE Expr 00530 const Expr& falseExpr() { return d_em->falseExpr(); } 00531 00532 //! Return TRUE Expr 00533 const Expr& trueExpr() { return d_em->trueExpr(); } 00534 00535 //! Create a new variable given its name and type 00536 /*! Add the variable to the database for resolving IDs in parseExpr 00537 */ 00538 Expr newVar(const std::string& name, const Type& type); 00539 //! Create a new named expression given its name, type, and definition 00540 /*! Add the definition to the database for resolving IDs in parseExpr 00541 */ 00542 Expr newVar(const std::string& name, const Type& type, const Expr& def); 00543 00544 //! Create a new uninterpreted function 00545 /*! Add the definition to the database for resolving IDs in parseExpr 00546 */ 00547 Op newFunction(const std::string& name, const Type& type, 00548 bool computeTransClosure); 00549 //! Create a new defined function 00550 /*! Add the definition to the database for resolving IDs in parseExpr 00551 */ 00552 Op newFunction(const std::string& name, const Type& type, const Expr& def); 00553 00554 //! Create and add a new bound variable to the stack, for parseExprOp(). 00555 /*! 00556 * The stack is popped automatically upon return from the 00557 * parseExprOp() which used this method. 00558 * 00559 * Bound variable names may repeat, in which case the latest 00560 * declaration takes precedence. 00561 */ 00562 Expr addBoundVar(const std::string& name, const Type& type); 00563 //! Create and add a new bound named def to the stack, for parseExprOp(). 00564 /*! 00565 * The stack is popped automatically upon return from the 00566 * parseExprOp() which used this method. 00567 * 00568 * Bound variable names may repeat, in which case the latest 00569 * declaration takes precedence. 00570 * 00571 * The type may be Null, but 'def' must always be a valid Expr 00572 */ 00573 Expr addBoundVar(const std::string& name, const Type& type, const Expr& def); 00574 00575 /*! @brief Lookup variable and return it and its type. Return NULL Expr if 00576 it doesn't exist yet. */ 00577 Expr lookupVar(const std::string& name, Type* type); 00578 00579 //! Create a new uninterpreted type with the given name 00580 /*! Add the name to the global variable database d_globals 00581 */ 00582 Type newTypeExpr(const std::string& name); 00583 //! Create a new type abbreviation with the given name 00584 Type newTypeExpr(const std::string& name, const Type& def); 00585 00586 //! Resolve an identifier, for use in parseExprOp() 00587 /*! 00588 * First, search the bound variable stack, and if the name is not 00589 * found, search the global constant and type declarations. 00590 * 00591 * \return an expression to use in place of the identifier, or Null 00592 * if cannot resolve the name. 00593 */ 00594 Expr resolveID(const std::string& name); 00595 00596 //! Install name as a new identifier associated with Expr e 00597 void installID(const std::string& name, const Expr& e); 00598 00599 Theorem typePred(const Expr& e); 00600 00601 //! e: T ==> |- [all the subtype-related facts for e and its subterms] 00602 Theorem subtypePredicate(const Expr& e); 00603 00604 /*@}*/ // End of Common Type and Expr Methods 00605 00606 /***************************************************************************/ 00607 /*! 00608 *\name Commonly Used Proof Rules 00609 *\anchor theory_api_core_proof_rules 00610 *@{ 00611 */ 00612 /***************************************************************************/ 00613 00614 //! ==> a == a 00615 Theorem reflexivityRule(const Expr& a) 00616 { return d_commonRules->reflexivityRule(a); } 00617 00618 //! a1 == a2 ==> a2 == a1 00619 Theorem symmetryRule(const Theorem& a1_eq_a2) 00620 { return d_commonRules->symmetryRule(a1_eq_a2); } 00621 00622 //! (a1 == a2) & (a2 == a3) ==> (a1 == a3) 00623 Theorem transitivityRule(const Theorem& a1_eq_a2, 00624 const Theorem& a2_eq_a3) 00625 { return d_commonRules->transitivityRule(a1_eq_a2, a2_eq_a3); } 00626 00627 //! (c_1 == d_1) & ... & (c_n == d_n) ==> op(c_1,...,c_n) == op(d_1,...,d_n) 00628 Theorem substitutivityRule(const Op& op, 00629 const std::vector<Theorem>& thms) 00630 { return d_commonRules->substitutivityRule(op, thms); } 00631 00632 //! Optimized: only positions which changed are included 00633 Theorem substitutivityRule(const Expr& e, 00634 const std::vector<unsigned>& changed, 00635 const std::vector<Theorem>& thms) 00636 { return d_commonRules->substitutivityRule(e, changed, thms); } 00637 00638 //! e1 AND (e1 IFF e2) ==> e2 00639 Theorem iffMP(const Theorem& e1, const Theorem& e1_iff_e2) { 00640 return d_commonRules->iffMP(e1, e1_iff_e2); 00641 } 00642 00643 //! ==> AND(e1,e2) IFF [simplified expr] 00644 Theorem rewriteAnd(const Expr& e) { 00645 return d_commonRules->rewriteAnd(e); 00646 } 00647 00648 //! ==> OR(e1,...,en) IFF [simplified expr] 00649 Theorem rewriteOr(const Expr& e) { 00650 return d_commonRules->rewriteOr(e); 00651 } 00652 00653 /*@}*/ // End of Commonly Used Proof Rules 00654 00655 00656 }; 00657 00658 /*@}*/ // End of group Theories 00659 00660 } 00661 00662 #endif