/********************* */ /*! \file normal_form.h ** \verbatim ** Top contributors (to current version): ** Tim King, Morgan Deters, Mathias Preiner ** This file is part of the CVC4 project. ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief [[ Add one-line brief description here ]] ** ** [[ Add lengthier description here ]] ** \todo document this file **/ #include "cvc4_private.h" #ifndef CVC4__THEORY__ARITH__NORMAL_FORM_H #define CVC4__THEORY__ARITH__NORMAL_FORM_H #include #include #include "base/output.h" #include "expr/node.h" #include "expr/node_self_iterator.h" #include "theory/arith/delta_rational.h" #include "util/rational.h" namespace CVC4 { namespace theory { namespace arith { /***********************************************/ /***************** Normal Form *****************/ /***********************************************/ /***********************************************/ /** * Section 1: Languages * The normal form for arithmetic nodes is defined by the language * accepted by the following BNFs with some guard conditions. * (The guard conditions are in Section 3 for completeness.) * * variable := n * where * n.isVar() or is foreign * n.getType() \in {Integer, Real} * * constant := n * where * n.getKind() == kind::CONST_RATIONAL * * var_list := variable | (* [variable]) * where * len [variable] >= 2 * isSorted varOrder [variable] * * monomial := constant | var_list | (* constant' var_list') * where * \f$ constant' \not\in {0,1} \f$ * * polynomial := monomial' | (+ [monomial]) * where * len [monomial] >= 2 * isStrictlySorted monoOrder [monomial] * forall (\x -> x != 0) [monomial] * * rational_cmp := (|><| qpolynomial constant) * where * |><| is GEQ, or GT * not (exists constantMonomial (monomialList qpolynomial)) * (exists realMonomial (monomialList qpolynomial)) * abs(monomialCoefficient (head (monomialList qpolynomial))) == 1 * * integer_cmp := (>= zpolynomial constant) * where * not (exists constantMonomial (monomialList zpolynomial)) * (forall integerMonomial (monomialList zpolynomial)) * the gcd of all numerators of coefficients is 1 * the denominator of all coefficients and the constant is 1 * the leading coefficient is positive * * rational_eq := (= qvarlist qpolynomial) * where * let allMonomials = (cons qvarlist (monomialList zpolynomial)) * let variableMonomials = (drop constantMonomial allMonomials) * isStrictlySorted variableMonomials * exists realMonomial variableMonomials * is not empty qvarlist * * integer_eq := (= zmonomial zpolynomial) * where * let allMonomials = (cons zmonomial (monomialList zpolynomial)) * let variableMonomials = (drop constantMonomial allMonomials) * not (constantMonomial zmonomial) * (forall integerMonomial allMonomials) * isStrictlySorted variableMonomials * the gcd of all numerators of coefficients is 1 * the denominator of all coefficients and the constant is 1 * the coefficient of monomial is positive * the value of the coefficient of monomial is minimal in variableMonomials * * comparison := TRUE | FALSE * | rational_cmp | (not rational_cmp) * | rational_eq | (not rational_eq) * | integer_cmp | (not integer_cmp) * | integer_eq | (not integer_eq) * * Normal Form for terms := polynomial * Normal Form for atoms := comparison */ /** * Section 2: Helper Classes * The langauges accepted by each of these defintions * roughly corresponds to one of the following helper classes: * Variable * Constant * VarList * Monomial * Polynomial * Comparison * * Each of the classes obeys the following contracts/design decisions: * -Calling isMember(Node node) on a node returns true iff that node is a * a member of the language. Note: isMember is O(n). * -Calling isNormalForm() on a helper class object returns true iff that * helper class currently represents a normal form object. * -If isNormalForm() is false, then this object must have been made * using a mk*() factory function. * -If isNormalForm() is true, calling getNode() on all of these classes * returns a node that would be accepted by the corresponding language. * And if isNormalForm() is false, returns Node::null(). * -Each of the classes is immutable. * -Public facing constuctors have a 1-to-1 correspondence with one of * production rules in the above grammar. * -Public facing constuctors are required to fail in debug mode when the * guards of the production rule are not strictly met. * For example: Monomial(Constant(1),VarList(Variable(x))) must fail. * -When a class has a Class parseClass(Node node) function, * if isMember(node) is true, the function is required to return an instance * of the helper class, instance, s.t. instance.getNode() == node. * And if isMember(node) is false, this throws an assertion failure in debug * mode and has undefined behaviour if not in debug mode. * -Only public facing constructors, parseClass(node), and mk*() functions are * considered privileged functions for the helper class. * -Only privileged functions may use private constructors, and access * private data members. * -All non-privileged functions are considered utility functions and * must use a privileged function in order to create an instance of the class. */ /** * Section 3: Guard Conditions Misc. * * * variable_order x y = * if (meta_kind_variable x) and (meta_kind_variable y) * then node_order x y * else if (meta_kind_variable x) * then false * else if (meta_kind_variable y) * then true * else node_order x y * * var_list_len vl = * match vl with * variable -> 1 * | (* [variable]) -> len [variable] * * order res = * match res with * Empty -> (0,Node::null()) * | NonEmpty(vl) -> (var_list_len vl, vl) * * var_listOrder a b = tuple_cmp (order a) (order b) * * monomialVarList monomial = * match monomial with * constant -> Empty * | var_list -> NonEmpty(var_list) * | (* constant' var_list') -> NonEmpty(var_list') * * monoOrder m0 m1 = var_listOrder (monomialVarList m0) (monomialVarList m1) * * integerMonomial mono = * forall varHasTypeInteger (monomialVarList mono) * * realMonomial mono = not (integerMonomial mono) * * constantMonomial monomial = * match monomial with * constant -> true * | var_list -> false * | (* constant' var_list') -> false * * monomialCoefficient monomial = * match monomial with * constant -> constant * | var_list -> Constant(1) * | (* constant' var_list') -> constant' * * monomialList polynomial = * match polynomial with * monomial -> monomial::[] * | (+ [monomial]) -> [monomial] */ /** * A NodeWrapper is a class that is a thinly veiled container of a Node object. */ class NodeWrapper { private: Node node; public: NodeWrapper(Node n) : node(n) {} const Node& getNode() const { return node; } };/* class NodeWrapper */ class Variable : public NodeWrapper { public: Variable(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); } // TODO: check if it's a theory leaf also static bool isMember(Node n) { Kind k = n.getKind(); switch (k) { case kind::CONST_RATIONAL: return false; case kind::INTS_DIVISION: case kind::INTS_MODULUS: case kind::DIVISION: case kind::INTS_DIVISION_TOTAL: case kind::INTS_MODULUS_TOTAL: case kind::DIVISION_TOTAL: return isDivMember(n); case kind::EXPONENTIAL: case kind::SINE: case kind::COSINE: case kind::TANGENT: case kind::COSECANT: case kind::SECANT: case kind::COTANGENT: case kind::ARCSINE: case kind::ARCCOSINE: case kind::ARCTANGENT: case kind::ARCCOSECANT: case kind::ARCSECANT: case kind::ARCCOTANGENT: case kind::SQRT: case kind::PI: return isTranscendentalMember(n); case kind::ABS: case kind::TO_INTEGER: // Treat to_int as a variable; it is replaced in early preprocessing // by a variable. return true; default: return isLeafMember(n); } } static bool isLeafMember(Node n); static bool isDivMember(Node n); bool isDivLike() const{ return isDivMember(getNode()); } static bool isTranscendentalMember(Node n); bool isNormalForm() { return isMember(getNode()); } bool isIntegral() const { return getNode().getType().isInteger(); } bool isMetaKindVariable() const { return getNode().isVar(); } bool operator<(const Variable& v) const { VariableNodeCmp cmp; return cmp(this->getNode(), v.getNode()); } struct VariableNodeCmp { static inline int cmp(const Node& n, const Node& m) { if ( n == m ) { return 0; } // this is now slightly off of the old variable order. bool nIsInteger = n.getType().isInteger(); bool mIsInteger = m.getType().isInteger(); if(nIsInteger == mIsInteger){ bool nIsVariable = n.isVar(); bool mIsVariable = m.isVar(); if(nIsVariable == mIsVariable){ if(n < m){ return -1; }else{ Assert(n != m); return 1; } }else{ if(nIsVariable){ return -1; // nIsVariable => !mIsVariable }else{ return 1; // !nIsVariable => mIsVariable } } }else{ Assert(nIsInteger != mIsInteger); if(nIsInteger){ return 1; // nIsInteger => !mIsInteger }else{ return -1; // !nIsInteger => mIsInteger } } } bool operator()(const Node& n, const Node& m) const { return VariableNodeCmp::cmp(n,m) < 0; } }; bool operator==(const Variable& v) const { return getNode() == v.getNode();} size_t getComplexity() const; };/* class Variable */ class Constant : public NodeWrapper { public: Constant(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); } static bool isMember(Node n) { return n.getKind() == kind::CONST_RATIONAL; } bool isNormalForm() { return isMember(getNode()); } static Constant mkConstant(Node n) { Assert(n.getKind() == kind::CONST_RATIONAL); return Constant(n); } static Constant mkConstant(const Rational& rat); static Constant mkZero() { return mkConstant(Rational(0)); } static Constant mkOne() { return mkConstant(Rational(1)); } const Rational& getValue() const { return getNode().getConst(); } static int absCmp(const Constant& a, const Constant& b); bool isIntegral() const { return getValue().isIntegral(); } int sgn() const { return getValue().sgn(); } bool isZero() const { return sgn() == 0; } bool isNegative() const { return sgn() < 0; } bool isPositive() const { return sgn() > 0; } bool isOne() const { return getValue() == 1; } Constant operator*(const Rational& other) const { return mkConstant(getValue() * other); } Constant operator*(const Constant& other) const { return mkConstant(getValue() * other.getValue()); } Constant operator+(const Constant& other) const { return mkConstant(getValue() + other.getValue()); } Constant operator-() const { return mkConstant(-getValue()); } Constant inverse() const{ Assert(!isZero()); return mkConstant(getValue().inverse()); } bool operator<(const Constant& other) const { return getValue() < other.getValue(); } bool operator==(const Constant& other) const { //Rely on node uniqueness. return getNode() == other.getNode(); } Constant abs() const { if(isNegative()){ return -(*this); }else{ return (*this); } } uint32_t length() const{ Assert(isIntegral()); return getValue().getNumerator().length(); } size_t getComplexity() const; };/* class Constant */ template inline Node makeNode(Kind k, GetNodeIterator start, GetNodeIterator end) { NodeBuilder<> nb(k); while(start != end) { nb << (*start).getNode(); ++start; } return Node(nb); }/* makeNode(Kind, iterator, iterator) */ /** * A VarList is a sorted list of variables representing a product. * If the VarList is empty, it represents an empty product or 1. * If the VarList has size 1, it represents a single variable. * * A non-sorted VarList can never be successfully made in debug mode. */ class VarList : public NodeWrapper { private: static Node multList(const std::vector& list) { Assert(list.size() >= 2); return makeNode(kind::NONLINEAR_MULT, list.begin(), list.end()); } VarList() : NodeWrapper(Node::null()) {} VarList(Node n); typedef expr::NodeSelfIterator internal_iterator; internal_iterator internalBegin() const { if(singleton()){ return expr::NodeSelfIterator::self(getNode()); }else{ return getNode().begin(); } } internal_iterator internalEnd() const { if(singleton()){ return expr::NodeSelfIterator::selfEnd(getNode()); }else{ return getNode().end(); } } public: class iterator : public std::iterator { private: internal_iterator d_iter; public: explicit iterator(internal_iterator i) : d_iter(i) {} inline Variable operator*() { return Variable(*d_iter); } bool operator==(const iterator& i) { return d_iter == i.d_iter; } bool operator!=(const iterator& i) { return d_iter != i.d_iter; } iterator operator++() { ++d_iter; return *this; } iterator operator++(int) { return iterator(d_iter++); } }; iterator begin() const { return iterator(internalBegin()); } iterator end() const { return iterator(internalEnd()); } Variable getHead() const { Assert(!empty()); return *(begin()); } VarList(Variable v) : NodeWrapper(v.getNode()) { Assert(isSorted(begin(), end())); } VarList(const std::vector& l) : NodeWrapper(multList(l)) { Assert(l.size() >= 2); Assert(isSorted(begin(), end())); } static bool isMember(Node n); bool isNormalForm() const { return !empty(); } static VarList mkEmptyVarList() { return VarList(); } /** There are no restrictions on the size of l */ static VarList mkVarList(const std::vector& l) { if(l.size() == 0) { return mkEmptyVarList(); } else if(l.size() == 1) { return VarList((*l.begin()).getNode()); } else { return VarList(l); } } bool empty() const { return getNode().isNull(); } bool singleton() const { return !empty() && getNode().getKind() != kind::NONLINEAR_MULT; } int size() const { if(singleton()) return 1; else return getNode().getNumChildren(); } static VarList parseVarList(Node n); VarList operator*(const VarList& vl) const; int cmp(const VarList& vl) const; bool operator<(const VarList& vl) const { return cmp(vl) < 0; } bool operator==(const VarList& vl) const { return cmp(vl) == 0; } bool isIntegral() const { for(iterator i = begin(), e=end(); i != e; ++i ){ Variable var = *i; if(!var.isIntegral()){ return false; } } return true; } size_t getComplexity() const; private: bool isSorted(iterator start, iterator end); };/* class VarList */ /** Constructors have side conditions. Use the static mkMonomial functions instead. */ class Monomial : public NodeWrapper { private: Constant constant; VarList varList; Monomial(Node n, const Constant& c, const VarList& vl): NodeWrapper(n), constant(c), varList(vl) { Assert(!c.isZero() || vl.empty()); Assert(c.isZero() || !vl.empty()); Assert(!c.isOne() || !multStructured(n)); } static Node makeMultNode(const Constant& c, const VarList& vl) { Assert(!c.isZero()); Assert(!c.isOne()); Assert(!vl.empty()); return NodeManager::currentNM()->mkNode(kind::MULT, c.getNode(), vl.getNode()); } static bool multStructured(Node n) { return n.getKind() == kind::MULT && n[0].getKind() == kind::CONST_RATIONAL && n.getNumChildren() == 2; } Monomial(const Constant& c): NodeWrapper(c.getNode()), constant(c), varList(VarList::mkEmptyVarList()) { } Monomial(const VarList& vl): NodeWrapper(vl.getNode()), constant(Constant::mkConstant(1)), varList(vl) { Assert(!varList.empty()); } Monomial(const Constant& c, const VarList& vl): NodeWrapper(makeMultNode(c,vl)), constant(c), varList(vl) { Assert(!c.isZero()); Assert(!c.isOne()); Assert(!varList.empty()); Assert(multStructured(getNode())); } public: static bool isMember(TNode n); /** Makes a monomial with no restrictions on c and vl. */ static Monomial mkMonomial(const Constant& c, const VarList& vl); /** If vl is empty, this make one. */ static Monomial mkMonomial(const VarList& vl); static Monomial mkMonomial(const Constant& c){ return Monomial(c); } static Monomial mkMonomial(const Variable& v){ return Monomial(VarList(v)); } static Monomial parseMonomial(Node n); static Monomial mkZero() { return Monomial(Constant::mkConstant(0)); } static Monomial mkOne() { return Monomial(Constant::mkConstant(1)); } const Constant& getConstant() const { return constant; } const VarList& getVarList() const { return varList; } bool isConstant() const { return varList.empty(); } bool isZero() const { return constant.isZero(); } bool coefficientIsOne() const { return constant.isOne(); } bool absCoefficientIsOne() const { return coefficientIsOne() || constant.getValue() == -1; } bool constantIsPositive() const { return getConstant().isPositive(); } Monomial operator*(const Rational& q) const; Monomial operator*(const Constant& c) const; Monomial operator*(const Monomial& mono) const; Monomial operator-() const{ return (*this) * Rational(-1); } int cmp(const Monomial& mono) const { return getVarList().cmp(mono.getVarList()); } bool operator<(const Monomial& vl) const { return cmp(vl) < 0; } bool operator==(const Monomial& vl) const { return cmp(vl) == 0; } static bool isSorted(const std::vector& m) { return std::is_sorted(m.begin(), m.end()); } static bool isStrictlySorted(const std::vector& m) { return isSorted(m) && std::adjacent_find(m.begin(),m.end()) == m.end(); } static void sort(std::vector& m); static void combineAdjacentMonomials(std::vector& m); /** * The variable product */ bool integralVariables() const { return getVarList().isIntegral(); } /** * The coefficient of the monomial is integral. */ bool integralCoefficient() const { return getConstant().isIntegral(); } /** * A Monomial is an "integral" monomial if the constant is integral. */ bool isIntegral() const { return integralCoefficient() && integralVariables(); } /** Returns true if the VarList is a product of at least 2 Variables.*/ bool isNonlinear() const { return getVarList().size() >= 2; } /** * Given a sorted list of monomials, this function transforms this * into a strictly sorted list of monomials that does not contain zero. */ //static std::vector sumLikeTerms(const std::vector& monos); int absCmp(const Monomial& other) const{ return getConstant().getValue().absCmp(other.getConstant().getValue()); } // bool absLessThan(const Monomial& other) const{ // return getConstant().abs() < other.getConstant().abs(); // } uint32_t coefficientLength() const{ return getConstant().length(); } void print() const; static void printList(const std::vector& list); size_t getComplexity() const; };/* class Monomial */ class SumPair; class Comparison;; class Polynomial : public NodeWrapper { private: bool d_singleton; Polynomial(TNode n) : NodeWrapper(n), d_singleton(Monomial::isMember(n)) { Assert(isMember(getNode())); } static Node makePlusNode(const std::vector& m) { Assert(m.size() >= 2); return makeNode(kind::PLUS, m.begin(), m.end()); } typedef expr::NodeSelfIterator internal_iterator; internal_iterator internalBegin() const { if(singleton()){ return expr::NodeSelfIterator::self(getNode()); }else{ return getNode().begin(); } } internal_iterator internalEnd() const { if(singleton()){ return expr::NodeSelfIterator::selfEnd(getNode()); }else{ return getNode().end(); } } bool singleton() const { return d_singleton; } public: static bool isMember(TNode n); class iterator : public std::iterator { private: internal_iterator d_iter; public: explicit iterator(internal_iterator i) : d_iter(i) {} inline Monomial operator*() { return Monomial::parseMonomial(*d_iter); } bool operator==(const iterator& i) { return d_iter == i.d_iter; } bool operator!=(const iterator& i) { return d_iter != i.d_iter; } iterator operator++() { ++d_iter; return *this; } iterator operator++(int) { return iterator(d_iter++); } }; iterator begin() const { return iterator(internalBegin()); } iterator end() const { return iterator(internalEnd()); } Polynomial(const Monomial& m): NodeWrapper(m.getNode()), d_singleton(true) {} Polynomial(const std::vector& m): NodeWrapper(makePlusNode(m)), d_singleton(false) { Assert(m.size() >= 2); Assert(Monomial::isStrictlySorted(m)); } static Polynomial mkPolynomial(const Constant& c){ return Polynomial(Monomial::mkMonomial(c)); } static Polynomial mkPolynomial(const Variable& v){ return Polynomial(Monomial::mkMonomial(v)); } static Polynomial mkPolynomial(const std::vector& m) { if(m.size() == 0) { return Polynomial(Monomial::mkZero()); } else if(m.size() == 1) { return Polynomial((*m.begin())); } else { return Polynomial(m); } } static Polynomial parsePolynomial(Node n) { return Polynomial(n); } static Polynomial mkZero() { return Polynomial(Monomial::mkZero()); } static Polynomial mkOne() { return Polynomial(Monomial::mkOne()); } bool isZero() const { return singleton() && (getHead().isZero()); } bool isConstant() const { return singleton() && (getHead().isConstant()); } bool containsConstant() const { return getHead().isConstant(); } uint32_t size() const{ if(singleton()){ return 1; }else{ Assert(getNode().getKind() == kind::PLUS); return getNode().getNumChildren(); } } Monomial getHead() const { return *(begin()); } Polynomial getTail() const { Assert(!singleton()); iterator tailStart = begin(); ++tailStart; std::vector subrange; std::copy(tailStart, end(), std::back_inserter(subrange)); return mkPolynomial(subrange); } Monomial minimumVariableMonomial() const; bool variableMonomialAreStrictlyGreater(const Monomial& m) const; void printList() const { if(Debug.isOn("normal-form")){ Debug("normal-form") << "start list" << std::endl; for(iterator i = begin(), oend = end(); i != oend; ++i) { const Monomial& m =*i; m.print(); } Debug("normal-form") << "end list" << std::endl; } } /** A Polynomial is an "integral" polynomial if all of the monomials are integral. */ bool allIntegralVariables() const { for(iterator i = begin(), e=end(); i!=e; ++i){ if(!(*i).integralVariables()){ return false; } } return true; } /** * A Polynomial is an "integral" polynomial if all of the monomials are integral * and all of the coefficients are Integral. */ bool isIntegral() const { for(iterator i = begin(), e=end(); i!=e; ++i){ if(!(*i).isIntegral()){ return false; } } return true; } static Polynomial sumPolynomials(const std::vector& polynomials); /** Returns true if the polynomial contains a non-linear monomial.*/ bool isNonlinear() const; /** * Selects a minimal monomial in the polynomial by the absolute value of * the coefficient. */ Monomial selectAbsMinimum() const; /** Returns true if the absolute value of the head coefficient is one. */ bool leadingCoefficientIsAbsOne() const; bool leadingCoefficientIsPositive() const; bool denominatorLCMIsOne() const; bool numeratorGCDIsOne() const; bool signNormalizedReducedSum() const { return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne(); } /** * Returns the Least Common Multiple of the denominators of the coefficients * of the monomials. */ Integer denominatorLCM() const; /** * Returns the GCD of the numerators of the monomials. * Requires this to be an isIntegral() polynomial. */ Integer numeratorGCD() const; /** * Returns the GCD of the coefficients of the monomials. * Requires this to be an isIntegral() polynomial. */ Integer gcd() const; /** z must divide all of the coefficients of the polynomial. */ Polynomial exactDivide(const Integer& z) const; Polynomial operator+(const Polynomial& vl) const; Polynomial operator-(const Polynomial& vl) const; Polynomial operator-() const{ return (*this) * Rational(-1); } Polynomial operator*(const Rational& q) const; Polynomial operator*(const Constant& c) const; Polynomial operator*(const Monomial& mono) const; Polynomial operator*(const Polynomial& poly) const; /** * Viewing the integer polynomial as a list [(* coeff_i mono_i)] * The quotient and remainder of p divided by the non-zero integer z is: * q := [(* floor(coeff_i/z) mono_i )] * r := [(* rem(coeff_i/z) mono_i)] * computeQR(p,z) returns the node (+ q r). * * q and r are members of the Polynomial class. * For example: * computeQR( p = (+ 5 (* 3 x) (* 8 y)) , z = 2) returns * (+ (+ 2 x (* 4 y)) (+ 1 x)) */ static Node computeQR(const Polynomial& p, const Integer& z); /** Returns the coefficient associated with the VarList in the polynomial. */ Constant getCoefficient(const VarList& vl) const; uint32_t maxLength() const{ iterator i = begin(), e=end(); if( i == e){ return 1; }else{ uint32_t max = (*i).coefficientLength(); ++i; for(; i!=e; ++i){ uint32_t curr = (*i).coefficientLength(); if(curr > max){ max = curr; } } return max; } } uint32_t numMonomials() const { if( getNode().getKind() == kind::PLUS ){ return getNode().getNumChildren(); }else if(isZero()){ return 0; }else{ return 1; } } const Rational& asConstant() const{ Assert(isConstant()); return getNode().getConst(); //return getHead().getConstant().getValue(); } bool isVarList() const { if(singleton()){ return VarList::isMember(getNode()); }else{ return false; } } VarList asVarList() const { Assert(isVarList()); return getHead().getVarList(); } size_t getComplexity() const; friend class SumPair; friend class Comparison; /** Returns a node that if asserted ensures v is the abs of this polynomial.*/ Node makeAbsCondition(Variable v){ return makeAbsCondition(v, *this); } /** Returns a node that if asserted ensures v is the abs of p.*/ static Node makeAbsCondition(Variable v, Polynomial p); };/* class Polynomial */ /** * SumPair is a utility class that extends polynomials for use in computations. * A SumPair is always a combination of (+ p c) where * c is a constant and p is a polynomial such that p = 0 or !p.containsConstant(). * * These are a useful utility for representing the equation p = c as (+ p -c) where the pair * is known to implicitly be equal to 0. * * SumPairs do not have unique representations due to the potential for p = 0. * This makes them inappropriate for normal forms. */ class SumPair : public NodeWrapper { private: static Node toNode(const Polynomial& p, const Constant& c){ return NodeManager::currentNM()->mkNode(kind::PLUS, p.getNode(), c.getNode()); } SumPair(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); } public: SumPair(const Polynomial& p): NodeWrapper(toNode(p, Constant::mkConstant(0))) { Assert(isNormalForm()); } SumPair(const Polynomial& p, const Constant& c): NodeWrapper(toNode(p, c)) { Assert(isNormalForm()); } static bool isMember(TNode n) { if(n.getKind() == kind::PLUS && n.getNumChildren() == 2){ if(Constant::isMember(n[1])){ if(Polynomial::isMember(n[0])){ Polynomial p = Polynomial::parsePolynomial(n[0]); return p.isZero() || (!p.containsConstant()); }else{ return false; } }else{ return false; } }else{ return false; } } bool isNormalForm() const { return isMember(getNode()); } Polynomial getPolynomial() const { return Polynomial::parsePolynomial(getNode()[0]); } Constant getConstant() const { return Constant::mkConstant((getNode())[1]); } SumPair operator+(const SumPair& other) const { return SumPair(getPolynomial() + other.getPolynomial(), getConstant() + other.getConstant()); } SumPair operator*(const Constant& c) const { return SumPair(getPolynomial() * c, getConstant() * c); } SumPair operator-(const SumPair& other) const { return (*this) + (other * Constant::mkConstant(-1)); } static SumPair mkSumPair(const Polynomial& p); static SumPair mkSumPair(const Variable& var){ return SumPair(Polynomial::mkPolynomial(var)); } static SumPair parseSumPair(TNode n){ return SumPair(n); } bool isIntegral() const{ return getConstant().isIntegral() && getPolynomial().isIntegral(); } bool isConstant() const { return getPolynomial().isZero(); } bool isZero() const { return getConstant().isZero() && isConstant(); } uint32_t size() const{ return getPolynomial().size(); } bool isNonlinear() const{ return getPolynomial().isNonlinear(); } /** * Returns the greatest common divisor of gcd(getPolynomial()) and getConstant(). * The SumPair must be integral. */ Integer gcd() const { Assert(isIntegral()); return (getPolynomial().gcd()).gcd(getConstant().getValue().getNumerator()); } uint32_t maxLength() const { Assert(isIntegral()); return std::max(getPolynomial().maxLength(), getConstant().length()); } static SumPair mkZero() { return SumPair(Polynomial::mkZero(), Constant::mkConstant(0)); } static Node computeQR(const SumPair& sp, const Integer& div); };/* class SumPair */ /* class OrderedPolynomialPair { */ /* private: */ /* Polynomial d_first; */ /* Polynomial d_second; */ /* public: */ /* OrderedPolynomialPair(const Polynomial& f, const Polynomial& s) */ /* : d_first(f), */ /* d_second(s) */ /* {} */ /* /\** Returns the first part of the pair. *\/ */ /* const Polynomial& getFirst() const { */ /* return d_first; */ /* } */ /* /\** Returns the second part of the pair. *\/ */ /* const Polynomial& getSecond() const { */ /* return d_second; */ /* } */ /* OrderedPolynomialPair operator*(const Constant& c) const; */ /* OrderedPolynomialPair operator+(const Polynomial& p) const; */ /* /\** Returns true if both of the polynomials are constant. *\/ */ /* bool isConstant() const; */ /* /\** */ /* * Evaluates an isConstant() ordered pair as if */ /* * (k getFirst() getRight()) */ /* *\/ */ /* bool evaluateConstant(Kind k) const; */ /* /\** */ /* * Returns the Least Common Multiple of the monomials */ /* * on the lefthand side and the constant on the right. */ /* *\/ */ /* Integer denominatorLCM() const; */ /* /\** Constructs a SumPair. *\/ */ /* SumPair toSumPair() const; */ /* OrderedPolynomialPair divideByGCD() const; */ /* OrderedPolynomialPair multiplyConstant(const Constant& c) const; */ /* /\** */ /* * Returns true if all of the variables are integers, */ /* * and the coefficients are integers. */ /* *\/ */ /* bool isIntegral() const; */ /* /\** Returns true if all of the variables are integers. *\/ */ /* bool allIntegralVariables() const { */ /* return getFirst().allIntegralVariables() && getSecond().allIntegralVariables(); */ /* } */ /* }; */ class Comparison : public NodeWrapper { private: static Node toNode(Kind k, const Polynomial& l, const Constant& c); static Node toNode(Kind k, const Polynomial& l, const Polynomial& r); Comparison(TNode n); /** * Creates a node in normal form equivalent to (= l 0). * All variables in l are integral. */ static Node mkIntEquality(const Polynomial& l); /** * Creates a comparison equivalent to (k l 0). * k is either GT or GEQ. * All variables in l are integral. */ static Node mkIntInequality(Kind k, const Polynomial& l); /** * Creates a node equivalent to (= l 0). * It is not the case that all variables in l are integral. */ static Node mkRatEquality(const Polynomial& l); /** * Creates a comparison equivalent to (k l 0). * k is either GT or GEQ. * It is not the case that all variables in l are integral. */ static Node mkRatInequality(Kind k, const Polynomial& l); public: Comparison(bool val) : NodeWrapper(NodeManager::currentNM()->mkConst(val)) { } /** * Given a literal to TheoryArith return a single kind to * to indicate its underlying structure. * The function returns the following in each case: * - (K left right) -> K where is either EQUAL, GT, or GEQ * - (CONST_BOOLEAN b) -> CONST_BOOLEAN * - (NOT (EQUAL left right)) -> DISTINCT * - (NOT (GT left right)) -> LEQ * - (NOT (GEQ left right)) -> LT * If none of these match, it returns UNDEFINED_KIND. */ static Kind comparisonKind(TNode literal); Kind comparisonKind() const { return comparisonKind(getNode()); } static Comparison mkComparison(Kind k, const Polynomial& l, const Polynomial& r); /** Returns true if the comparison is a boolean constant. */ bool isBoolean() const; /** * Returns true if the comparison is either a boolean term, * in integer normal form or mixed normal form. */ bool isNormalForm() const; private: bool isNormalGT() const; bool isNormalGEQ() const; bool isNormalLT() const; bool isNormalLEQ() const; bool isNormalEquality() const; bool isNormalDistinct() const; bool isNormalEqualityOrDisequality() const; bool allIntegralVariables() const { return getLeft().allIntegralVariables() && getRight().allIntegralVariables(); } bool rightIsConstant() const; public: Polynomial getLeft() const; Polynomial getRight() const; /* /\** Normal form check if at least one variable is real. *\/ */ /* bool isMixedCompareNormalForm() const; */ /* /\** Normal form check if at least one variable is real. *\/ */ /* bool isMixedEqualsNormalForm() const; */ /* /\** Normal form check is all variables are integer.*\/ */ /* bool isIntegerCompareNormalForm() const; */ /* /\** Normal form check is all variables are integer.*\/ */ /* bool isIntegerEqualsNormalForm() const; */ /** * Returns true if all of the variables are integers, the coefficients are integers, * and the right hand coefficient is an integer. */ bool debugIsIntegral() const; static Comparison parseNormalForm(TNode n); inline static bool isNormalAtom(TNode n){ Comparison parse = Comparison::parseNormalForm(n); return parse.isNormalForm(); } size_t getComplexity() const; SumPair toSumPair() const; Polynomial normalizedVariablePart() const; DeltaRational normalizedDeltaRational() const; };/* class Comparison */ }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ #endif /* CVC4__THEORY__ARITH__NORMAL_FORM_H */