LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/arith - operator_elim.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 300 310 96.8 %
Date: 2026-07-22 10:35:40 Functions: 8 8 100.0 %
Branches: 137 211 64.9 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * Implementation of operator elimination for arithmetic.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "theory/arith/operator_elim.h"
      14                 :            : 
      15                 :            : #include <sstream>
      16                 :            : 
      17                 :            : #include "expr/bound_var_manager.h"
      18                 :            : #include "options/arith_options.h"
      19                 :            : #include "proof/proof.h"
      20                 :            : #include "proof/proof_node_manager.h"
      21                 :            : #include "proof/trust_id.h"
      22                 :            : #include "smt/env.h"
      23                 :            : #include "smt/logic_exception.h"
      24                 :            : #include "theory/arith/arith_utilities.h"
      25                 :            : #include "theory/arith/nl/poly_conversion.h"
      26                 :            : #include "theory/rewriter.h"
      27                 :            : #include "theory/theory.h"
      28                 :            : 
      29                 :            : using namespace cvc5::internal::kind;
      30                 :            : 
      31                 :            : namespace cvc5::internal {
      32                 :            : namespace theory {
      33                 :            : namespace arith {
      34                 :            : 
      35                 :      28663 : OperatorElim::OperatorElim(Env& env) : EnvObj(env), d_lemmaMap(userContext()) {}
      36                 :            : 
      37                 :     856683 : TrustNode OperatorElim::eliminate(Node n,
      38                 :            :                                   std::vector<SkolemLemma>& lems,
      39                 :            :                                   bool partialOnly)
      40                 :            : {
      41 [ -  + ][ -  + ]:     856683 :   Assert(rewrite(n) == n);
                 [ -  - ]
      42                 :     856683 :   NodeManager* nm = nodeManager();
      43                 :     856683 :   std::vector<std::pair<Node, Node>> klems;
      44                 :     856683 :   bool wasNonLinear = false;
      45                 :     856683 :   Node nn = eliminateOperators(nm, n, klems, partialOnly, wasNonLinear);
      46         [ +  + ]:     856683 :   if (nn == n)
      47                 :            :   {
      48                 :     849243 :     return TrustNode::null();
      49                 :            :   }
      50                 :            :   // logic exception if non-linear
      51         [ +  + ]:       7440 :   if (wasNonLinear)
      52                 :            :   {
      53         [ +  + ]:       3672 :     if (logicInfo().isLinear())
      54                 :            :     {
      55         [ +  - ]:         10 :       Trace("arith-logic") << "ERROR: Non-linear term in linear logic: " << n
      56                 :          5 :                            << std::endl;
      57                 :          5 :       std::stringstream serr;
      58                 :          5 :       serr << "A non-linear fact was asserted to arithmetic in a linear logic."
      59                 :          5 :            << std::endl;
      60                 :          5 :       serr << "The fact in question: " << n << std::endl;
      61                 :          5 :       throw LogicException(serr.str());
      62                 :          5 :     }
      63                 :            :   }
      64                 :            :   // if transcendental, we don't eliminate if not expert
      65         [ +  + ]:       7435 :   if (isTranscendentalKind(n.getKind()))
      66                 :            :   {
      67         [ -  + ]:        133 :     if (!options().arith.arithExp)
      68                 :            :     {
      69                 :          0 :       return TrustNode::null();
      70                 :            :     }
      71                 :            :   }
      72                 :            :   // should only be a single lemma, if there is one
      73 [ -  + ][ -  + ]:       7435 :   Assert(klems.size() <= 1);
                 [ -  - ]
      74         [ +  + ]:      12591 :   for (std::pair<Node, Node>& p : klems)
      75                 :            :   {
      76                 :            :     // each skolem lemma can be justified by this class
      77                 :       5156 :     lems.emplace_back(mkSkolemLemma(p.first, p.second, n));
      78                 :            :   }
      79                 :            :   // we can provide a proof for the rewrite as well
      80                 :       7435 :   return TrustNode::mkTrustRewrite(n, nn, this);
      81                 :     856688 : }
      82                 :            : 
      83                 :     860842 : Node OperatorElim::eliminateOperators(NodeManager* nm,
      84                 :            :                                       Node node,
      85                 :            :                                       std::vector<std::pair<Node, Node>>& lems,
      86                 :            :                                       bool partialOnly,
      87                 :            :                                       bool& wasNonLinear)
      88                 :            : {
      89         [ +  - ]:     860842 :   Trace("arith-op-elim") << "node: " << node << std::endl;
      90                 :     860842 :   SkolemManager* sm = nm->getSkolemManager();
      91                 :     860842 :   Kind k = node.getKind();
      92 [ +  + ][ +  + ]:     860842 :   switch (k)
         [ +  + ][ +  + ]
            [ +  + ][ + ]
      93                 :            :   {
      94                 :        941 :     case Kind::TO_INTEGER:
      95                 :            :     case Kind::IS_INTEGER:
      96                 :            :     {
      97         [ +  + ]:        941 :       if (partialOnly)
      98                 :            :       {
      99                 :            :         // not eliminating total operators
     100                 :         31 :         return node;
     101                 :            :       }
     102                 :            :       // node[0] - 1 < toIntSkolem <= node[0]
     103                 :            :       // -1 < toIntSkolem - node[0] <= 0
     104                 :            :       // 0 <= node[0] - toIntSkolem < 1
     105                 :       1820 :       Node pterm = nm->mkNode(Kind::TO_INTEGER, node[0]);
     106                 :        910 :       Node v = sm->mkPurifySkolem(pterm);
     107                 :        910 :       Node vr = nm->mkNode(Kind::TO_REAL, v);
     108                 :        910 :       Node one = nm->mkConstReal(Rational(1));
     109                 :        910 :       Node zero = nm->mkConstReal(Rational(0));
     110                 :       1820 :       Node diff = nm->mkNode(Kind::SUB, node[0], vr);
     111                 :       1820 :       Node lem = mkInRange(diff, zero, one);
     112                 :        910 :       lems.emplace_back(lem, v);
     113         [ +  + ]:        910 :       if (k == Kind::IS_INTEGER)
     114                 :            :       {
     115                 :        526 :         return nm->mkNode(Kind::EQUAL, node[0], vr);
     116                 :            :       }
     117 [ -  + ][ -  + ]:        384 :       Assert(k == Kind::TO_INTEGER);
                 [ -  - ]
     118                 :        384 :       return v;
     119                 :        910 :     }
     120                 :         60 :     case Kind::INTS_LOG2:
     121                 :            :     {
     122         [ +  + ]:         60 :       if (partialOnly)
     123                 :            :       {
     124                 :            :         // not eliminating total operators
     125                 :          2 :         return node;
     126                 :            :       }
     127                 :            :       // for a fresh skolem v, the elimination is:
     128                 :            :       // (int.log2 x) --> v, with lemmas:
     129                 :            :       // (=> (> x 0) (and (<= (int.pow2 v) x) (< x (* 2 (int.pow2 v)))))
     130                 :            :       // (=> (<= x 0) (= v 0))
     131                 :        116 :       Node zero = nm->mkConstInt(Integer(0));
     132                 :        116 :       Node one = nm->mkConstInt(Integer(1));
     133                 :         58 :       Node x = node[0];
     134                 :         58 :       Node v = sm->mkPurifySkolem(node);
     135                 :        116 :       Node sv = nm->mkNode(Kind::ADD, v, one);
     136                 :         58 :       Node ptv = nm->mkNode(Kind::POW2, v);
     137                 :         58 :       Node ptv1 = nm->mkNode(Kind::POW2, sv);
     138                 :        116 :       Node pos_assumption = nm->mkNode(Kind::LT, zero, x);
     139                 :        116 :       Node pos_prop1 = nm->mkNode(Kind::LEQ, ptv, x);
     140                 :        116 :       Node pos_prop2 = nm->mkNode(Kind::LT, x, ptv1);
     141                 :        116 :       Node pos_prop = nm->mkNode(Kind::AND, pos_prop1, pos_prop2);
     142                 :        116 :       Node pos_lem = nm->mkNode(Kind::IMPLIES, pos_assumption, pos_prop);
     143                 :            : 
     144                 :         58 :       Node neg_assumption = nm->mkNode(Kind::NOT, pos_assumption);
     145                 :        116 :       Node neg_prop = nm->mkNode(Kind::EQUAL, v, zero);
     146                 :        116 :       Node neg_lem = nm->mkNode(Kind::IMPLIES, neg_assumption, neg_prop);
     147                 :        116 :       Node lem = nm->mkNode(Kind::AND, pos_lem, neg_lem);
     148                 :         58 :       lems.emplace_back(lem, v);
     149                 :            : 
     150         [ +  - ]:         58 :       Trace("arith-op-elim") << "INTS_LOG2: node" << node << std::endl;
     151         [ +  - ]:         58 :       Trace("arith-op-elim") << "INTS_LOG2: x" << x << std::endl;
     152         [ +  - ]:         58 :       Trace("arith-op-elim") << "INTS_LOG2: v" << v << std::endl;
     153         [ +  - ]:         58 :       Trace("arith-op-elim") << "INTS_LOG2: lem" << lem << std::endl;
     154                 :         58 :       return v;
     155                 :         58 :     }
     156                 :       6517 :     case Kind::INTS_DIVISION_TOTAL:
     157                 :            :     case Kind::INTS_MODULUS_TOTAL:
     158                 :            :     {
     159         [ +  + ]:       6517 :       if (partialOnly)
     160                 :            :       {
     161                 :            :         // not eliminating total operators
     162                 :         91 :         return node;
     163                 :            :       }
     164                 :            :       // we use the purification skolem for div
     165                 :       6426 :       Node den = node[1];
     166                 :       6426 :       Node num = node[0];
     167                 :      12852 :       Node pterm = nm->mkNode(Kind::INTS_DIVISION_TOTAL, node[0], node[1]);
     168                 :       6426 :       Node v = sm->mkPurifySkolem(pterm);
     169                 :            :       // make the corresponding lemma
     170                 :       6426 :       Node lem;
     171                 :      12852 :       Node leqNum = nm->mkNode(Kind::LEQ, nm->mkNode(Kind::MULT, den, v), num);
     172         [ +  + ]:       6426 :       if (den.isConst())
     173                 :            :       {
     174                 :       5028 :         const Rational& rat = den.getConst<Rational>();
     175 [ +  - ][ +  - ]:       5028 :         Assert(!num.isConst() && rat.sgn() != 0);
         [ -  + ][ -  + ]
                 [ -  - ]
     176                 :      10056 :         lem = nm->mkNode(
     177                 :            :             Kind::AND,
     178                 :            :             leqNum,
     179                 :      10056 :             nm->mkNode(
     180                 :            :                 Kind::LT,
     181                 :            :                 num,
     182                 :      10056 :                 nm->mkNode(
     183                 :            :                     Kind::MULT,
     184                 :            :                     den,
     185                 :      10056 :                     nm->mkNode(Kind::ADD,
     186                 :            :                                v,
     187         [ +  - ]:      15084 :                                nm->mkConstInt(Rational(rat > 0 ? 1 : -1))))));
     188                 :            :       }
     189                 :            :       else
     190                 :            :       {
     191                 :       1398 :         wasNonLinear = true;
     192 [ +  + ][ -  - ]:       6990 :         lem = nm->mkNode(
     193                 :            :             Kind::AND,
     194 [ +  + ][ -  - ]:       6990 :             {nm->mkNode(
     195                 :            :                  Kind::IMPLIES,
     196                 :       2796 :                  {nm->mkNode(Kind::GT, den, nm->mkConstInt(Rational(0))),
     197                 :       2796 :                   nm->mkNode(
     198                 :            :                       Kind::AND,
     199                 :            :                       leqNum,
     200                 :       2796 :                       nm->mkNode(Kind::LT,
     201                 :            :                                  num,
     202                 :       2796 :                                  nm->mkNode(Kind::MULT,
     203                 :            :                                             den,
     204                 :       2796 :                                             nm->mkNode(Kind::ADD,
     205                 :            :                                                        v,
     206                 :       2796 :                                                        nm->mkConstInt(
     207                 :       2796 :                                                            Rational(1))))))}),
     208 [ +  + ][ -  - ]:       6990 :              nm->mkNode(
     209                 :            :                  Kind::IMPLIES,
     210                 :       2796 :                  {nm->mkNode(Kind::LT, den, nm->mkConstInt(Rational(0))),
     211                 :       2796 :                   nm->mkNode(
     212                 :            :                       Kind::AND,
     213                 :            :                       leqNum,
     214                 :       2796 :                       nm->mkNode(
     215                 :            :                           Kind::LT,
     216                 :            :                           num,
     217                 :       2796 :                           nm->mkNode(
     218                 :            :                               Kind::MULT,
     219                 :            :                               den,
     220                 :       2796 :                               nm->mkNode(Kind::ADD,
     221                 :            :                                          v,
     222                 :       4194 :                                          nm->mkConstInt(Rational(-1))))))})});
     223                 :            :       }
     224                 :            :       // add the skolem lemma to lems
     225                 :       6426 :       lems.emplace_back(lem, v);
     226         [ +  - ]:       6426 :       Trace("arith-op-elim") << "lem " << lem << std::endl;
     227         [ +  + ]:       6426 :       if (k == Kind::INTS_MODULUS_TOTAL)
     228                 :            :       {
     229                 :       8012 :         Node nn = nm->mkNode(Kind::SUB, num, nm->mkNode(Kind::MULT, den, v));
     230                 :       4006 :         return nn;
     231                 :       4006 :       }
     232                 :       2420 :       return v;
     233                 :       6426 :     }
     234                 :       1002 :     case Kind::DIVISION_TOTAL:
     235                 :            :     {
     236         [ +  + ]:       1002 :       if (partialOnly)
     237                 :            :       {
     238                 :            :         // not eliminating total operators
     239                 :        103 :         return node;
     240                 :            :       }
     241                 :        899 :       Node num = node[0];
     242                 :        899 :       Node den = node[1];
     243         [ -  + ]:        899 :       if (den.isConst())
     244                 :            :       {
     245                 :            :         // No need to eliminate here, can eliminate via rewriting later.
     246                 :            :         // Moreover, rewriting may change the type of this node from real to
     247                 :            :         // int, which impacts certain issues with subtyping.
     248                 :          0 :         return node;
     249                 :            :       }
     250                 :        899 :       wasNonLinear = true;
     251                 :       1798 :       Node rw = nm->mkNode(k, num, den);
     252                 :        899 :       Node v = sm->mkPurifySkolem(rw);
     253         [ +  + ]:        899 :       if (num.getType().isInteger())
     254                 :            :       {
     255                 :         89 :         num = nm->mkNode(Kind::TO_REAL, num);
     256                 :            :       }
     257         [ +  + ]:        899 :       if (den.getType().isInteger())
     258                 :            :       {
     259                 :        131 :         den = nm->mkNode(Kind::TO_REAL, den);
     260                 :            :       }
     261                 :       2697 :       Node lem = nm->mkNode(
     262                 :            :           Kind::IMPLIES,
     263                 :       1798 :           {den.eqNode(mkZero(den.getType())).negate(),
     264                 :       1798 :            nm->mkNode(Kind::EQUAL, nm->mkNode(Kind::MULT, den, v), num)});
     265                 :        899 :       lems.emplace_back(lem, v);
     266                 :        899 :       return v;
     267                 :            :       break;
     268                 :        899 :     }
     269                 :       1161 :     case Kind::DIVISION:
     270                 :            :     {
     271                 :       1161 :       Node num = node[0];
     272                 :       1161 :       Node den = node[1];
     273                 :       2322 :       Node ret = nm->mkNode(Kind::DIVISION_TOTAL, num, den);
     274 [ +  + ][ +  - ]:       1161 :       if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
                 [ +  - ]
     275                 :            :       {
     276                 :       1161 :         wasNonLinear = true;
     277                 :       1161 :         Node divByZeroNum = getArithSkolemApp(nm, num, SkolemId::DIV_BY_ZERO);
     278                 :       2322 :         Node denEq0 = nm->mkNode(Kind::EQUAL, den, mkZero(den.getType()));
     279                 :       1161 :         ret = nm->mkNode(Kind::ITE, denEq0, divByZeroNum, ret);
     280                 :       1161 :       }
     281                 :       1161 :       return ret;
     282                 :            :       break;
     283                 :       1161 :     }
     284                 :            : 
     285                 :        672 :     case Kind::INTS_DIVISION:
     286                 :            :     {
     287                 :            :       // partial function: integer div
     288                 :        672 :       Node num = node[0];
     289                 :        672 :       Node den = node[1];
     290                 :       1344 :       Node ret = nm->mkNode(Kind::INTS_DIVISION_TOTAL, num, den);
     291 [ +  + ][ +  - ]:        672 :       if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
                 [ +  - ]
     292                 :            :       {
     293                 :        672 :         wasNonLinear = true;
     294                 :            :         Node intDivByZeroNum =
     295                 :        672 :             getArithSkolemApp(nm, num, SkolemId::INT_DIV_BY_ZERO);
     296                 :       1344 :         Node denEq0 = nm->mkNode(Kind::EQUAL, den, nm->mkConstInt(Rational(0)));
     297                 :        672 :         ret = nm->mkNode(Kind::ITE, denEq0, intDivByZeroNum, ret);
     298                 :        672 :       }
     299                 :        672 :       return ret;
     300                 :            :       break;
     301                 :        672 :     }
     302                 :            : 
     303                 :        924 :     case Kind::INTS_MODULUS:
     304                 :            :     {
     305                 :            :       // partial function: mod
     306                 :        924 :       Node num = node[0];
     307                 :        924 :       Node den = node[1];
     308                 :       1848 :       Node ret = nm->mkNode(Kind::INTS_MODULUS_TOTAL, num, den);
     309 [ +  + ][ +  - ]:        924 :       if (!den.isConst() || den.getConst<Rational>().sgn() == 0)
                 [ +  - ]
     310                 :            :       {
     311                 :        924 :         wasNonLinear = true;
     312                 :        924 :         Node modZeroNum = getArithSkolemApp(nm, num, SkolemId::MOD_BY_ZERO);
     313                 :       1848 :         Node denEq0 = nm->mkNode(Kind::EQUAL, den, nm->mkConstInt(Rational(0)));
     314                 :        924 :         ret = nm->mkNode(Kind::ITE, denEq0, modZeroNum, ret);
     315                 :        924 :       }
     316                 :        924 :       return ret;
     317                 :            :       break;
     318                 :        924 :     }
     319                 :            : 
     320                 :        318 :     case Kind::ABS:
     321                 :            :     {
     322                 :       1272 :       return nm->mkNode(
     323                 :            :           Kind::ITE,
     324                 :        636 :           {nm->mkNode(Kind::LT,
     325                 :            :                       node[0],
     326                 :        636 :                       nm->mkConstRealOrInt(node[0].getType(), Rational(0))),
     327                 :        636 :            nm->mkNode(Kind::NEG, node[0]),
     328 [ +  + ][ -  - ]:       1908 :            node[0]});
     329                 :            :       break;
     330                 :            :     }
     331                 :        248 :     case Kind::SQRT:
     332                 :            :     case Kind::ARCSINE:
     333                 :            :     case Kind::ARCCOSINE:
     334                 :            :     case Kind::ARCTANGENT:
     335                 :            :     case Kind::ARCCOSECANT:
     336                 :            :     case Kind::ARCSECANT:
     337                 :            :     case Kind::ARCCOTANGENT:
     338                 :            :     {
     339         [ +  + ]:        248 :       if (partialOnly)
     340                 :            :       {
     341                 :            :         // not eliminating total operators
     342                 :         20 :         return node;
     343                 :            :       }
     344                 :        228 :       wasNonLinear = true;
     345                 :            :       // We eliminate these functions using an uninterpreted function via
     346                 :            :       // the skolem id TRANSCENDENTAL_PURIFY.
     347                 :            :       // Make (lambda ((x Real)) (f x)) for this function, using the bound
     348                 :            :       // variable manager to ensure this function is always the same.
     349                 :        228 :       BoundVarManager* bvm = nm->getBoundVarManager();
     350                 :            :       Node x = bvm->mkBoundVar(
     351                 :        456 :           BoundVarId::ARITH_TR_PURIFY, node.getOperator(), "x", nm->realType());
     352                 :            :       Node lam =
     353                 :        684 :           nm->mkNode(Kind::LAMBDA,
     354                 :        456 :                      {nm->mkNode(Kind::BOUND_VAR_LIST, x), nm->mkNode(k, x)});
     355                 :        228 :       Node fun = sm->mkSkolemFunction(SkolemId::TRANSCENDENTAL_PURIFY, lam);
     356                 :            :       // Make (@TRANSCENDENTAL_PURIFY t), where t is node[0]
     357                 :        456 :       Node var = nm->mkNode(Kind::APPLY_UF, fun, node[0]);
     358                 :        228 :       Node lem;
     359         [ +  + ]:        228 :       if (k == Kind::SQRT)
     360                 :            :       {
     361                 :         95 :         Node zero = nm->mkConstReal(Rational(0));
     362                 :        190 :         Node eq = nm->mkNode(Kind::MULT, var, var).eqNode(node[0]);
     363                 :        190 :         Node resNonNeg = nm->mkNode(Kind::GEQ, var, zero);
     364                 :            : 
     365                 :            :         // (sqrt x) reduces to:
     366                 :            :         // (=> (>= x 0.0) (and (>= y 0.0) (= (* y y) x))
     367                 :            :         // where y is (@TRANSCENDENTAL_PURIFY x).
     368                 :            :         //
     369                 :            :         // This makes sure that the reduction still behaves like a function,
     370                 :            :         // otherwise the reduction of (x = -1) ^ (sqrt(x) != sqrt(-1)) would be
     371                 :            :         // satisfiable.
     372 [ +  + ][ -  - ]:        475 :         lem = nm->mkNode(Kind::IMPLIES,
     373                 :        190 :                          {nm->mkNode(Kind::GEQ, node[0], zero),
     374                 :        285 :                           nm->mkNode(Kind::AND, resNonNeg, eq)});
     375                 :         95 :       }
     376                 :            :       else
     377                 :            :       {
     378                 :        133 :         Node pi = mkPi(nm);
     379                 :            : 
     380                 :            :         // range of the skolem
     381                 :        133 :         Node rlem;
     382 [ +  + ][ +  + ]:        133 :         if (k == Kind::ARCSINE || k == Kind::ARCTANGENT
     383         [ -  + ]:         47 :             || k == Kind::ARCCOSECANT)
     384                 :            :         {
     385                 :        172 :           Node half = nm->mkConstReal(Rational(1) / Rational(2));
     386                 :        172 :           Node pi2 = nm->mkNode(Kind::MULT, half, pi);
     387                 :            :           Node npi2 =
     388                 :        172 :               nm->mkNode(Kind::MULT, nm->mkConstReal(Rational(-1)), pi2);
     389                 :            :           // -pi/2 < var <= pi/2
     390 [ +  + ][ -  - ]:        430 :           rlem = nm->mkNode(Kind::AND,
     391                 :        172 :                             {nm->mkNode(Kind::LT, npi2, var),
     392                 :        258 :                              nm->mkNode(Kind::LEQ, var, pi2)});
     393                 :         86 :         }
     394                 :            :         else
     395                 :            :         {
     396                 :            :           // 0 <= var < pi
     397 [ +  + ][ -  - ]:        235 :           rlem = nm->mkNode(
     398                 :            :               Kind::AND,
     399                 :         94 :               {nm->mkNode(Kind::LEQ, nm->mkConstReal(Rational(0)), var),
     400                 :        141 :                nm->mkNode(Kind::LT, var, pi)});
     401                 :            :         }
     402                 :        133 :         Node cond;
     403 [ +  + ][ +  + ]:        133 :         if (k == Kind::ARCSINE || k == Kind::ARCCOSINE || k == Kind::ARCSECANT
                 [ +  - ]
     404         [ -  + ]:         46 :             || k == Kind::ARCCOSECANT)
     405                 :            :         {
     406                 :            :           // -1 <= x <= 1
     407 [ +  + ][ -  - ]:        435 :           cond = nm->mkNode(
     408                 :            :               Kind::AND,
     409                 :        174 :               {nm->mkNode(Kind::GEQ, node[0], nm->mkConstReal(Rational(-1))),
     410                 :        261 :                nm->mkNode(Kind::LEQ, node[0], nm->mkConstReal(Rational(1)))});
     411 [ +  - ][ -  + ]:         87 :           if (k == Kind::ARCSECANT || k == Kind::ARCCOSECANT)
     412                 :            :           {
     413                 :          0 :             cond = cond.notNode();
     414                 :            :           }
     415                 :            :         }
     416                 :            : 
     417                 :            :         Kind rk;
     418                 :            :         switch (k)
     419                 :            :         {
     420                 :         40 :           case Kind::ARCSINE: rk = Kind::SINE; break;
     421                 :         47 :           case Kind::ARCCOSINE: rk = Kind::COSINE; break;
     422                 :         46 :           case Kind::ARCTANGENT: rk = Kind::TANGENT; break;
     423                 :          0 :           case Kind::ARCCOSECANT: rk = Kind::COSECANT; break;
     424                 :          0 :           case Kind::ARCSECANT: rk = Kind::SECANT; break;
     425                 :          0 :           case Kind::ARCCOTANGENT: rk = Kind::COTANGENT; break;
     426                 :          0 :           default: Unreachable() << "Unexpected kind " << k;
     427                 :            :         }
     428                 :        133 :         Node invTerm = nm->mkNode(rk, var);
     429                 :        133 :         lem = nm->mkNode(Kind::AND, rlem, mkEquality(invTerm, node[0]));
     430         [ +  + ]:        133 :         if (!cond.isNull())
     431                 :            :         {
     432                 :         87 :           lem = nm->mkNode(Kind::IMPLIES, cond, lem);
     433                 :            :         }
     434         [ +  - ]:        266 :         Trace("arith-op-elim")
     435                 :        133 :             << "Elimination lemma " << lem << " for " << node << std::endl;
     436                 :        133 :       }
     437 [ -  + ][ -  + ]:        228 :       Assert(!lem.isNull());
                 [ -  - ]
     438                 :            :       // the skolem lemma is for the function
     439                 :        228 :       lems.emplace_back(lem, fun);
     440                 :        228 :       return var;
     441                 :        228 :     }
     442                 :          3 :     case Kind::REAL_ALGEBRAIC_NUMBER:
     443                 :            :     {
     444                 :          3 :       BoundVarManager* bvm = nm->getBoundVarManager();
     445                 :            :       Node v = bvm->mkBoundVar(
     446                 :          6 :           BoundVarId::REAL_ALGEBRAIC_NUMBER_WITNESS, node, "i", nm->realType());
     447                 :          3 :       Node w;
     448                 :            : #ifdef CVC5_POLY_IMP
     449                 :          6 :       w = PolyConverter::ran_to_node(
     450                 :          9 :           node.getOperator().getConst<RealAlgebraicNumber>(), v);
     451                 :            : #endif
     452                 :            :       // it should not be possible to define real algebraic numbers unless poly
     453                 :            :       // is enabled
     454 [ -  + ][ -  + ]:          3 :       Assert(!w.isNull());
                 [ -  - ]
     455                 :          3 :       return w;
     456                 :          3 :     }
     457                 :            :     // these are handled by rewriting
     458                 :     848996 :     default: break;
     459                 :            :   }
     460                 :     848996 :   return node;
     461                 :            : }
     462                 :            : 
     463                 :       4159 : Node OperatorElim::getAxiomFor(NodeManager* nm, const Node& n)
     464                 :            : {
     465                 :       4159 :   std::vector<std::pair<Node, Node>> klems;
     466                 :       4159 :   bool wasNonLinear = false;
     467                 :       4159 :   Node nn = eliminateOperators(nm, n, klems, false, wasNonLinear);
     468         [ -  + ]:       4159 :   if (nn == n)
     469                 :            :   {
     470                 :          0 :     return Node::null();
     471                 :            :   }
     472                 :       4159 :   Node eqLem = n.eqNode(nn);
     473                 :       4159 :   std::vector<Node> lemmas;
     474         [ +  + ]:       7524 :   for (const std::pair<Node, Node>& kl : klems)
     475                 :            :   {
     476                 :       3365 :     lemmas.emplace_back(kl.first);
     477                 :            :   }
     478         [ +  + ]:       4159 :   if (!lemmas.empty())
     479                 :            :   {
     480                 :       3365 :     Node axiom = nm->mkAnd(lemmas);
     481                 :       3365 :     return nm->mkNode(Kind::AND, eqLem, axiom);
     482                 :       3365 :   }
     483                 :        794 :   return eqLem;
     484                 :       4159 : }
     485                 :            : 
     486                 :       2757 : Node OperatorElim::getArithSkolemApp(NodeManager* nm, Node n, SkolemId id)
     487                 :            : {
     488                 :       2757 :   SkolemManager* sm = nm->getSkolemManager();
     489                 :       2757 :   Node skolem = sm->mkSkolemFunction(id);
     490                 :       2757 :   Assert(skolem.getType().isFunction()
     491                 :            :          && skolem.getType().getNumChildren() == 2);
     492                 :       2757 :   TypeNode argType = skolem.getType()[0];
     493 [ +  + ][ +  + ]:       2757 :   if (!argType.isInteger() && n.getType().isInteger())
         [ +  + ][ +  + ]
                 [ -  - ]
     494                 :            :   {
     495                 :        123 :     n = nm->mkNode(Kind::TO_REAL, n);
     496                 :            :   }
     497                 :       2757 :   skolem = nm->mkNode(Kind::APPLY_UF, skolem, n);
     498                 :       5514 :   return skolem;
     499                 :       2757 : }
     500                 :            : 
     501                 :       5156 : SkolemLemma OperatorElim::mkSkolemLemma(const Node& lem,
     502                 :            :                                         const Node& k,
     503                 :            :                                         const Node& n)
     504                 :            : {
     505                 :       5156 :   TrustNode tlem;
     506         [ +  + ]:       5156 :   if (d_env.isTheoryProofProducing())
     507                 :            :   {
     508                 :       2054 :     tlem = TrustNode::mkTrustLemma(lem, this);
     509                 :       2054 :     d_lemmaMap[lem] = n;
     510                 :            :   }
     511                 :            :   else
     512                 :            :   {
     513                 :       3102 :     tlem = TrustNode::mkTrustLemma(lem, nullptr);
     514                 :            :   }
     515                 :      10312 :   return SkolemLemma(tlem, k);
     516                 :       5156 : }
     517                 :            : 
     518                 :       2607 : std::shared_ptr<ProofNode> OperatorElim::getProofFor(Node f)
     519                 :            : {
     520                 :            :   // This class provides proofs for two things:
     521                 :            :   // (1) rewrites n --> nn during preprocessing,
     522                 :            :   // (2) the axioms A added when rewriting n ---> nn.
     523                 :            :   // The proof rule ARITH_REDUCTION proves things of the form:
     524                 :            :   //    (and (= n nn) A)
     525                 :            :   // where A may be omitted. We first determine which case we are in (whether
     526                 :            :   // being asked for a proof of a preprocessing rewrite or an axiom) and store
     527                 :            :   // the target term (n above) into tgt.
     528                 :       2607 :   context::CDHashMap<Node, Node>::iterator it = d_lemmaMap.find(f);
     529                 :       2607 :   Node tgt;
     530         [ +  + ]:       2607 :   if (it == d_lemmaMap.end())
     531                 :            :   {
     532         [ -  + ]:       1835 :     if (f.getKind() != Kind::EQUAL)
     533                 :            :     {
     534                 :          0 :       DebugUnhandled() << "arith::OperatorElim could not prove " << f;
     535                 :            :       return nullptr;
     536                 :            :     }
     537                 :            :     // target is the left hand side.
     538                 :       1835 :     tgt = f[0];
     539                 :            :   }
     540                 :            :   else
     541                 :            :   {
     542                 :            :     // target was stored in d_lemmaMap for an axiom.
     543                 :        772 :     tgt = it->second;
     544                 :            :   }
     545                 :       5214 :   CDProof cdp(d_env);
     546                 :       2607 :   Node res = getAxiomFor(nodeManager(), tgt);
     547                 :       5214 :   cdp.addStep(res, ProofRule::ARITH_REDUCTION, {}, {tgt});
     548                 :       2607 :   bool success = false;
     549                 :            :   // If the axiom was an AND, then the fact in question should be one of the
     550                 :            :   // conjuncts, in which case we do an AND_ELIM step.
     551         [ +  + ]:       2607 :   if (res.getKind() == Kind::AND)
     552                 :            :   {
     553 [ -  + ][ -  + ]:       2166 :     Assert(res.getNumChildren() == 2);
                 [ -  - ]
     554         [ +  - ]:       2938 :     for (size_t i = 0; i < 2; i++)
     555                 :            :     {
     556         [ +  + ]:       2938 :       if (res[i] == f)
     557                 :            :       {
     558                 :       2166 :         Node ni = nodeManager()->mkConstInt(i);
     559                 :       6498 :         cdp.addStep(f, ProofRule::AND_ELIM, {res}, {ni});
     560                 :       2166 :         success = true;
     561                 :       2166 :         break;
     562                 :       2166 :       }
     563                 :            :     }
     564                 :            :   }
     565                 :            :   else
     566                 :            :   {
     567                 :        441 :     success = (res == f);
     568                 :            :   }
     569                 :       2607 :   Assert(success) << "arith::OperatorElim could not prove " << f;
     570         [ -  + ]:       2607 :   if (!success)
     571                 :            :   {
     572                 :          0 :     return nullptr;
     573                 :            :   }
     574                 :       2607 :   return cdp.getProofFor(f);
     575                 :       2607 : }
     576                 :            : 
     577                 :        104 : std::string OperatorElim::identify() const { return "arith::OperatorElim"; }
     578                 :            : 
     579                 :            : }  // namespace arith
     580                 :            : }  // namespace theory
     581                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14