LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/arith/nl/coverings - cdcac.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 316 438 72.1 %
Date: 2026-07-07 10:35:32 Functions: 28 36 77.8 %
Branches: 169 322 52.5 %

           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                 :            :  * Implements the CDCAC approach as described in
      11                 :            :  * https://arxiv.org/pdf/2003.05633.pdf.
      12                 :            :  */
      13                 :            : 
      14                 :            : #include "theory/arith/nl/coverings/cdcac.h"
      15                 :            : 
      16                 :            : #ifdef CVC5_POLY_IMP
      17                 :            : 
      18                 :            : #include "options/arith_options.h"
      19                 :            : #include "theory/arith/nl/coverings/lazard_evaluation.h"
      20                 :            : #include "theory/arith/nl/coverings/projections.h"
      21                 :            : #include "theory/arith/nl/coverings/variable_ordering.h"
      22                 :            : #include "theory/arith/nl/nl_model.h"
      23                 :            : #include "theory/rewriter.h"
      24                 :            : #include "util/resource_manager.h"
      25                 :            : 
      26                 :            : using namespace cvc5::internal::kind;
      27                 :            : 
      28                 :            : namespace std {
      29                 :            : /** Generic streaming operator for std::vector. */
      30                 :            : template <typename T>
      31                 :          0 : std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
      32                 :            : {
      33                 :          0 :   cvc5::internal::container_to_stream(os, v);
      34                 :          0 :   return os;
      35                 :            : }
      36                 :            : }  // namespace std
      37                 :            : 
      38                 :            : namespace cvc5::internal {
      39                 :            : namespace theory {
      40                 :            : namespace arith {
      41                 :            : namespace nl {
      42                 :            : namespace coverings {
      43                 :            : 
      44                 :      34056 : CDCAC::CDCAC(Env& env, const std::vector<poly::Variable>& ordering)
      45                 :            :     : EnvObj(env),
      46                 :      34056 :       d_assignment(nodeManager()->getPolyContext()),
      47                 :      34056 :       d_constraints(nodeManager()->getPolyContext()),
      48                 :      34056 :       d_variableOrdering(ordering),
      49                 :      68112 :       d_varOrder(nodeManager()->getPolyContext())
      50                 :            : {
      51         [ +  + ]:      34056 :   if (d_env.isTheoryProofProducing())
      52                 :            :   {
      53                 :       7532 :     d_proof.reset(new CoveringsProofGenerator(env, userContext()));
      54                 :            :   }
      55                 :      34056 : }
      56                 :            : 
      57                 :        274 : void CDCAC::reset()
      58                 :            : {
      59                 :        274 :   d_constraints.reset();
      60                 :        274 :   d_assignment.clear();
      61                 :        274 :   d_nextIntervalId = 1;
      62                 :        274 : }
      63                 :            : 
      64                 :          0 : poly::detail::variable_printer CDCAC::get_stream_variable(
      65                 :            :     const poly::Variable& v)
      66                 :            : {
      67                 :          0 :   const poly::Context& ctx = nodeManager()->getPolyContext();
      68                 :          0 :   return stream_variable(ctx, v);
      69                 :            : }
      70                 :            : 
      71                 :          0 : std::vector<poly::detail::variable_printer> CDCAC::get_stream_variables(
      72                 :            :     const std::vector<poly::Variable>& vars)
      73                 :            : {
      74                 :          0 :   std::vector<poly::detail::variable_printer> result;
      75                 :          0 :   result.reserve(vars.size());
      76                 :            : 
      77                 :          0 :   const poly::Context& ctx = nodeManager()->getPolyContext();
      78                 :            : 
      79         [ -  - ]:          0 :   for (const poly::Variable& v : vars)
      80                 :            :   {
      81                 :          0 :     result.emplace_back(stream_variable(ctx, v));
      82                 :            :   }
      83                 :            : 
      84                 :          0 :   return result;
      85                 :          0 : }
      86                 :            : 
      87                 :        279 : void CDCAC::computeVariableOrdering()
      88                 :            : {
      89                 :            :   // Actually compute the variable ordering
      90                 :        558 :   d_variableOrdering = d_varOrder(d_constraints.getConstraints(),
      91                 :        279 :                                   VariableOrderingStrategy::BROWN);
      92         [ +  - ]:        558 :   Trace("cdcac") << "Variable ordering is now "
      93 [ -  + ][ -  - ]:        279 :                  << get_stream_variables(d_variableOrdering) << std::endl;
      94                 :            : 
      95                 :            :   // Write variable ordering back to libpoly.
      96                 :            :   lp_variable_order_t* vo =
      97                 :        279 :       nodeManager()->getPolyContext().get_variable_order();
      98                 :        279 :   lp_variable_order_clear(vo);
      99         [ +  + ]:       1313 :   for (const auto& v : d_variableOrdering)
     100                 :            :   {
     101                 :       1034 :     lp_variable_order_push(vo, v.get_internal());
     102                 :            :   }
     103                 :        279 : }
     104                 :            : 
     105                 :        272 : void CDCAC::retrieveInitialAssignment(NlModel& model, const Node& ran_variable)
     106                 :            : {
     107         [ +  - ]:        272 :   if (options().arith.nlCovLinearModel == options::nlCovLinearModelMode::NONE)
     108                 :        272 :     return;
     109                 :          0 :   d_initialAssignment.clear();
     110         [ -  - ]:          0 :   Trace("cdcac") << "Retrieving initial assignment:" << std::endl;
     111         [ -  - ]:          0 :   for (const auto& var : d_variableOrdering)
     112                 :            :   {
     113                 :          0 :     Node v = getConstraints().varMapper()(var);
     114                 :          0 :     Node val = model.computeConcreteModelValue(v);
     115                 :          0 :     poly::Value value = node_to_value(val, ran_variable);
     116         [ -  - ]:          0 :     Trace("cdcac") << "\t" << get_stream_variable(var) << " = " << value
     117                 :          0 :                    << std::endl;
     118                 :          0 :     d_initialAssignment.emplace_back(value);
     119                 :          0 :   }
     120                 :            : }
     121                 :       6412 : Constraints& CDCAC::getConstraints() { return d_constraints; }
     122                 :          0 : const Constraints& CDCAC::getConstraints() const { return d_constraints; }
     123                 :            : 
     124                 :        384 : const poly::Assignment& CDCAC::getModel() const { return d_assignment; }
     125                 :            : 
     126                 :        128 : const std::vector<poly::Variable>& CDCAC::getVariableOrdering() const
     127                 :            : {
     128                 :        128 :   return d_variableOrdering;
     129                 :            : }
     130                 :            : 
     131                 :       2665 : std::vector<CACInterval> CDCAC::getUnsatIntervals(std::size_t cur_variable)
     132                 :            : {
     133                 :       2665 :   std::vector<CACInterval> res;
     134                 :       2665 :   LazardEvaluation le(statisticsRegistry(), nodeManager()->getPolyContext());
     135                 :       2665 :   prepareRootIsolation(le, cur_variable);
     136         [ +  + ]:      47566 :   for (const auto& c : d_constraints.getConstraints())
     137                 :            :   {
     138                 :      44901 :     const poly::Polynomial& p = std::get<0>(c);
     139                 :      44901 :     poly::SignCondition sc = std::get<1>(c);
     140                 :      44901 :     const Node& n = std::get<2>(c);
     141                 :            : 
     142         [ +  + ]:      44901 :     if (main_variable(p) != d_variableOrdering[cur_variable])
     143                 :            :     {
     144                 :            :       // Constraint is in another variable, ignore it.
     145                 :      33129 :       continue;
     146                 :            :     }
     147                 :            : 
     148         [ +  - ]:      23544 :     Trace("cdcac") << "Infeasible intervals for " << p << " " << sc
     149                 :      11772 :                    << " 0 over " << d_assignment << std::endl;
     150                 :      11772 :     std::vector<poly::Interval> intervals;
     151         [ +  + ]:      11772 :     if (options().arith.nlCovLifting == options::nlCovLiftingMode::LAZARD)
     152                 :            :     {
     153                 :        572 :       intervals = le.infeasibleRegions(p, sc);
     154         [ -  + ]:        572 :       if (TraceIsOn("cdcac"))
     155                 :            :       {
     156                 :          0 :         auto reference = poly::infeasible_regions(p, d_assignment, sc);
     157         [ -  - ]:          0 :         Trace("cdcac") << "Lazard: " << intervals << std::endl;
     158         [ -  - ]:          0 :         Trace("cdcac") << "Regular: " << reference << std::endl;
     159                 :          0 :       }
     160                 :            :     }
     161                 :            :     else
     162                 :            :     {
     163                 :      11200 :       intervals = poly::infeasible_regions(p, d_assignment, sc);
     164                 :            :     }
     165         [ +  + ]:      24485 :     for (const auto& i : intervals)
     166                 :            :     {
     167         [ +  - ]:      12713 :       Trace("cdcac") << "-> " << i << std::endl;
     168                 :      12713 :       PolyVector l, u, m, d;
     169                 :      12713 :       m.add(p);
     170                 :      12713 :       m.pushDownPolys(d, d_variableOrdering[cur_variable]);
     171         [ +  + ]:      12713 :       if (!is_minus_infinity(get_lower(i))) l = m;
     172         [ +  + ]:      12713 :       if (!is_plus_infinity(get_upper(i))) u = m;
     173                 :      25426 :       res.emplace_back(CACInterval{d_nextIntervalId++, i, l, u, m, d, {n}});
     174         [ +  + ]:      12713 :       if (isProofEnabled())
     175                 :            :       {
     176                 :       9584 :         d_proof->addDirect(
     177                 :       4792 :             d_constraints.varMapper()(d_variableOrdering[cur_variable]),
     178                 :            :             d_constraints.varMapper(),
     179                 :            :             p,
     180                 :       2396 :             d_assignment,
     181                 :            :             i,
     182                 :            :             n,
     183                 :       2396 :             res.back().d_id);
     184                 :            :       }
     185                 :      12713 :     }
     186                 :      11772 :   }
     187                 :       2665 :   pruneRedundantIntervals(res);
     188                 :       5330 :   return res;
     189                 :       2665 : }
     190                 :            : 
     191                 :       4853 : bool CDCAC::sampleOutsideWithInitial(const std::vector<CACInterval>& infeasible,
     192                 :            :                                      poly::Value& sample,
     193                 :            :                                      std::size_t cur_variable)
     194                 :            : {
     195                 :       4853 :   if (options().arith.nlCovLinearModel != options::nlCovLinearModelMode::NONE
     196 [ -  + ][ -  - ]:       4853 :       && cur_variable < d_initialAssignment.size())
                 [ -  + ]
     197                 :            :   {
     198                 :          0 :     const poly::Value& suggested = d_initialAssignment[cur_variable];
     199         [ -  - ]:          0 :     for (const auto& i : infeasible)
     200                 :            :     {
     201         [ -  - ]:          0 :       if (poly::contains(i.d_interval, suggested))
     202                 :            :       {
     203                 :          0 :         if (options().arith.nlCovLinearModel
     204         [ -  - ]:          0 :             == options::nlCovLinearModelMode::INITIAL)
     205                 :            :         {
     206                 :          0 :           d_initialAssignment.clear();
     207                 :            :         }
     208                 :          0 :         return sampleOutside(infeasible, sample);
     209                 :            :       }
     210                 :            :     }
     211         [ -  - ]:          0 :     Trace("cdcac") << "Using suggested initial value" << std::endl;
     212                 :          0 :     sample = suggested;
     213                 :          0 :     return true;
     214                 :            :   }
     215                 :       4853 :   return sampleOutside(infeasible, sample);
     216                 :            : }
     217                 :            : 
     218                 :            : namespace {
     219                 :            : 
     220                 :            : /**
     221                 :            :  * This method follows the projection operator as detailed in algorithm 6 of
     222                 :            :  * 10.1016/j.jlamp.2020.100633, which mostly follows the projection operator due
     223                 :            :  * to McCallum. It uses all coefficients until one is either constant or does
     224                 :            :  * not vanish over the current assignment.
     225                 :            :  */
     226                 :       4064 : PolyVector requiredCoefficientsOriginal(const poly::Polynomial& p,
     227                 :            :                                         const poly::Assignment& assignment)
     228                 :            : {
     229                 :       4064 :   PolyVector res;
     230         [ +  - ]:       4066 :   for (long deg = degree(p); deg >= 0; --deg)
     231                 :            :   {
     232                 :       4066 :     auto coeff = coefficient(p, deg);
     233 [ -  + ][ -  + ]:       4066 :     Assert(poly::is_constant(coeff)
                 [ -  - ]
     234                 :            :            == lp_polynomial_is_constant(coeff.get_internal()));
     235         [ +  + ]:       4066 :     if (poly::is_constant(coeff)) break;
     236                 :        516 :     res.add(coeff);
     237         [ +  + ]:        516 :     if (evaluate_constraint(coeff, assignment, poly::SignCondition::NE))
     238                 :            :     {
     239                 :        514 :       break;
     240                 :            :     }
     241         [ +  + ]:       4066 :   }
     242                 :       4064 :   return res;
     243                 :          0 : }
     244                 :            : 
     245                 :            : /**
     246                 :            :  * This method follows the original projection operator due to Lazard from
     247                 :            :  * section 3 of 10.1007/978-1-4612-2628-4_29. It uses the leading and trailing
     248                 :            :  * coefficient, and makes some limited efforts to avoid them in certain cases:
     249                 :            :  * We avoid the leading coefficient if it is constant. We avoid the trailing
     250                 :            :  * coefficient if the leading coefficient does not vanish over the current
     251                 :            :  * assignment and the trailing coefficient is not constant.
     252                 :            :  */
     253                 :          0 : PolyVector requiredCoefficientsLazard(const poly::Polynomial& p,
     254                 :            :                                       const poly::Assignment& assignment)
     255                 :            : {
     256                 :          0 :   PolyVector res;
     257                 :          0 :   auto lc = poly::leading_coefficient(p);
     258         [ -  - ]:          0 :   if (poly::is_constant(lc)) return res;
     259                 :          0 :   res.add(lc);
     260         [ -  - ]:          0 :   if (evaluate_constraint(lc, assignment, poly::SignCondition::NE)) return res;
     261                 :          0 :   auto tc = poly::coefficient(p, 0);
     262         [ -  - ]:          0 :   if (poly::is_constant(tc)) return res;
     263                 :          0 :   res.add(tc);
     264                 :          0 :   return res;
     265                 :          0 : }
     266                 :            : 
     267                 :            : /**
     268                 :            :  * This method follows the enhancements from 10.1007/978-3-030-60026-6_8 for the
     269                 :            :  * projection operator due to Lazard, more specifically Section 3.3 and
     270                 :            :  * Definition 4. In essence, we can skip the trailing coefficient if we can show
     271                 :            :  * that p is not nullified by any n-1 dimensional point. The statement in the
     272                 :            :  * paper is slightly more general, but there is no simple way to have such a
     273                 :            :  * procedure T here. We simply try to show that T(f) = {} by using the extended
     274                 :            :  * rewriter to simplify (and (= f_i 0)) (f_i being the coefficients of f) to
     275                 :            :  * false.
     276                 :            :  */
     277                 :          0 : PolyVector requiredCoefficientsLazardModified(
     278                 :            :     NodeManager* nm,
     279                 :            :     const poly::Polynomial& p,
     280                 :            :     const poly::Assignment& assignment,
     281                 :            :     VariableMapper& vm,
     282                 :            :     Rewriter* rewriter)
     283                 :            : {
     284                 :          0 :   PolyVector res;
     285                 :          0 :   auto lc = poly::leading_coefficient(p);
     286                 :            :   // if leading coefficient is constant
     287         [ -  - ]:          0 :   if (poly::is_constant(lc)) return res;
     288                 :            :   // add leading coefficient
     289                 :          0 :   res.add(lc);
     290                 :          0 :   auto tc = poly::coefficient(p, 0);
     291                 :            :   // if trailing coefficient is constant
     292         [ -  - ]:          0 :   if (poly::is_constant(tc)) return res;
     293                 :            :   // if leading coefficient does not vanish over the current assignment
     294         [ -  - ]:          0 :   if (evaluate_constraint(lc, assignment, poly::SignCondition::NE)) return res;
     295                 :            : 
     296                 :            :   // construct phi := (and (= p_i 0)) with p_i the coefficients of p
     297                 :          0 :   std::vector<Node> conditions;
     298                 :          0 :   auto zero = nm->mkConstReal(Rational(0));
     299         [ -  - ]:          0 :   for (const auto& coeff : poly::coefficients(p))
     300                 :            :   {
     301                 :          0 :     conditions.emplace_back(NodeManager::mkNode(
     302                 :          0 :         Kind::EQUAL, nl::as_cvc_polynomial(nm, coeff, vm), zero));
     303                 :          0 :   }
     304                 :            :   // if phi is false (i.e. p can not vanish)
     305                 :          0 :   Node rewritten = rewriter->extendedRewrite(nm->mkAnd(conditions));
     306         [ -  - ]:          0 :   if (rewritten.isConst())
     307                 :            :   {
     308                 :          0 :     Assert(rewritten.getKind() == Kind::CONST_BOOLEAN);
     309                 :          0 :     Assert(!rewritten.getConst<bool>());
     310                 :          0 :     return res;
     311                 :            :   }
     312                 :            :   // otherwise add trailing coefficient as well
     313                 :          0 :   res.add(tc);
     314                 :          0 :   return res;
     315                 :          0 : }
     316                 :            : 
     317                 :            : }  // namespace
     318                 :            : 
     319                 :       4064 : PolyVector CDCAC::requiredCoefficients(const poly::Polynomial& p)
     320                 :            : {
     321         [ -  + ]:       4064 :   if (TraceIsOn("cdcac::projection"))
     322                 :            :   {
     323         [ -  - ]:          0 :     Trace("cdcac::projection")
     324                 :          0 :         << "Poly: " << p << " over " << d_assignment << std::endl;
     325         [ -  - ]:          0 :     Trace("cdcac::projection")
     326                 :          0 :         << "Lazard:   " << requiredCoefficientsLazard(p, d_assignment)
     327                 :          0 :         << std::endl;
     328         [ -  - ]:          0 :     Trace("cdcac::projection")
     329                 :          0 :         << "LMod: "
     330                 :          0 :         << requiredCoefficientsLazardModified(d_env.getNodeManager(),
     331                 :            :                                               p,
     332                 :          0 :                                               d_assignment,
     333                 :            :                                               d_constraints.varMapper(),
     334                 :          0 :                                               d_env.getRewriter())
     335                 :          0 :         << std::endl;
     336         [ -  - ]:          0 :     Trace("cdcac::projection")
     337                 :          0 :         << "Original: " << requiredCoefficientsOriginal(p, d_assignment)
     338                 :          0 :         << std::endl;
     339                 :            :   }
     340 [ +  - ][ -  - ]:       4064 :   switch (options().arith.nlCovProjection)
     341                 :            :   {
     342                 :       4064 :     case options::nlCovProjectionMode::MCCALLUM:
     343                 :       4064 :       return requiredCoefficientsOriginal(p, d_assignment);
     344                 :          0 :     case options::nlCovProjectionMode::LAZARD:
     345                 :          0 :       return requiredCoefficientsLazard(p, d_assignment);
     346                 :          0 :     case options::nlCovProjectionMode::LAZARDMOD:
     347                 :          0 :       return requiredCoefficientsLazardModified(d_env.getNodeManager(),
     348                 :            :                                                 p,
     349                 :          0 :                                                 d_assignment,
     350                 :            :                                                 d_constraints.varMapper(),
     351                 :          0 :                                                 d_env.getRewriter());
     352                 :          0 :     default:
     353                 :          0 :       DebugUnhandled();
     354                 :            :       return requiredCoefficientsOriginal(p, d_assignment);
     355                 :            :   }
     356                 :            : }
     357                 :            : 
     358                 :       2102 : PolyVector CDCAC::constructCharacterization(std::vector<CACInterval>& intervals)
     359                 :            : {
     360 [ -  + ][ -  + ]:       2102 :   Assert(!intervals.empty()) << "A covering can not be empty";
                 [ -  - ]
     361         [ +  - ]:       2102 :   Trace("cdcac") << "Constructing characterization now" << std::endl;
     362                 :       2102 :   PolyVector res;
     363                 :            : 
     364         [ +  + ]:       3831 :   for (std::size_t i = 0, n = intervals.size(); i < n - 1; ++i)
     365                 :            :   {
     366                 :       1729 :     coverings::makeFinestSquareFreeBasis(intervals[i], intervals[i + 1]);
     367                 :            :   }
     368                 :            : 
     369         [ +  + ]:       5933 :   for (const auto& i : intervals)
     370                 :            :   {
     371         [ +  - ]:       3831 :     Trace("cdcac") << "Considering " << i.d_interval << std::endl;
     372         [ +  - ]:       7662 :     Trace("cdcac") << "-> " << i.d_lowerPolys << " / " << i.d_upperPolys
     373                 :          0 :                    << " and " << i.d_mainPolys << " / " << i.d_downPolys
     374                 :       3831 :                    << std::endl;
     375         [ +  - ]:       3831 :     Trace("cdcac") << "-> " << i.d_origins << std::endl;
     376         [ +  + ]:      17048 :     for (const auto& p : i.d_downPolys)
     377                 :            :     {
     378                 :            :       // Add all polynomial from lower levels.
     379                 :      13217 :       res.add(p);
     380                 :            :     }
     381         [ +  + ]:       7895 :     for (const auto& p : i.d_mainPolys)
     382                 :            :     {
     383         [ +  - ]:       8128 :       Trace("cdcac::projection")
     384 [ -  + ][ -  - ]:       4064 :           << "Discriminant of " << p << " -> " << discriminant(p) << std::endl;
     385                 :            :       // Add all discriminants
     386                 :       4064 :       res.add(discriminant(p));
     387                 :            : 
     388                 :            :       // Add pairwise resultants
     389         [ +  + ]:       8828 :       for (const auto& q : i.d_mainPolys)
     390                 :            :       {
     391                 :            :         // avoid symmetric duplicates
     392         [ +  + ]:       4764 :         if (p >= q) continue;
     393                 :        350 :         res.add(resultant(p, q));
     394                 :            :       }
     395                 :            : 
     396         [ +  + ]:       4932 :       for (const auto& q : requiredCoefficients(p))
     397                 :            :       {
     398                 :            :         // Add all required coefficients
     399         [ +  - ]:       1736 :         Trace("cdcac::projection")
     400                 :        868 :             << "Coeff of " << p << " -> " << q << std::endl;
     401                 :        868 :         res.add(q);
     402                 :       4064 :       }
     403         [ +  + ]:       6027 :       for (const auto& q : i.d_lowerPolys)
     404                 :            :       {
     405         [ +  + ]:       1963 :         if (p == q) continue;
     406                 :            :         // Check whether p(s \times a) = 0 for some a <= l
     407         [ -  + ]:        234 :         if (!hasRootBelow(q, get_lower(i.d_interval))) continue;
     408         [ +  - ]:        468 :         Trace("cdcac::projection") << "Resultant of " << p << " and " << q
     409 [ -  + ][ -  - ]:        234 :                                    << " -> " << resultant(p, q) << std::endl;
     410                 :        234 :         res.add(resultant(p, q));
     411                 :            :       }
     412         [ +  + ]:       6057 :       for (const auto& q : i.d_upperPolys)
     413                 :            :       {
     414         [ +  + ]:       1993 :         if (p == q) continue;
     415                 :            :         // Check whether p(s \times a) = 0 for some a >= u
     416         [ -  + ]:        260 :         if (!hasRootAbove(q, get_upper(i.d_interval))) continue;
     417         [ +  - ]:        520 :         Trace("cdcac::projection") << "Resultant of " << p << " and " << q
     418 [ -  + ][ -  - ]:        260 :                                    << " -> " << resultant(p, q) << std::endl;
     419                 :        260 :         res.add(resultant(p, q));
     420                 :            :       }
     421                 :            :     }
     422                 :            :   }
     423                 :            : 
     424         [ +  + ]:       3831 :   for (std::size_t i = 0, n = intervals.size(); i < n - 1; ++i)
     425                 :            :   {
     426                 :            :     // Add resultants of consecutive intervals.
     427         [ +  + ]:       3462 :     for (const auto& p : intervals[i].d_upperPolys)
     428                 :            :     {
     429         [ +  + ]:       3466 :       for (const auto& q : intervals[i + 1].d_lowerPolys)
     430                 :            :       {
     431         [ +  - ]:       3466 :         Trace("cdcac::projection") << "Resultant of " << p << " and " << q
     432 [ -  + ][ -  - ]:       1733 :                                    << " -> " << resultant(p, q) << std::endl;
     433                 :       1733 :         res.add(resultant(p, q));
     434                 :            :       }
     435                 :            :     }
     436                 :            :   }
     437                 :            : 
     438                 :       2102 :   res.reduce();
     439                 :       2102 :   res.makeFinestSquareFreeBasis();
     440                 :            : 
     441                 :       2102 :   return res;
     442                 :          0 : }
     443                 :            : 
     444                 :       2102 : CACInterval CDCAC::intervalFromCharacterization(
     445                 :            :     const PolyVector& characterization,
     446                 :            :     std::size_t cur_variable,
     447                 :            :     const poly::Value& sample)
     448                 :            : {
     449                 :       2102 :   PolyVector l;
     450                 :       2102 :   PolyVector u;
     451                 :       2102 :   PolyVector m;
     452                 :       2102 :   PolyVector d;
     453                 :            : 
     454         [ +  + ]:      11804 :   for (const auto& p : characterization)
     455                 :            :   {
     456                 :            :     // Add polynomials to main
     457                 :       9702 :     m.add(p);
     458                 :            :   }
     459                 :            :   // Push lower-dimensional polys to down
     460                 :       2102 :   m.pushDownPolys(d, d_variableOrdering[cur_variable]);
     461                 :            : 
     462                 :            :   // Collect -oo, all roots, oo
     463                 :            : 
     464                 :       2102 :   LazardEvaluation le(statisticsRegistry(), nodeManager()->getPolyContext());
     465                 :       2102 :   prepareRootIsolation(le, cur_variable);
     466                 :       2102 :   std::vector<poly::Value> roots;
     467                 :       2102 :   roots.emplace_back(poly::Value::minus_infty());
     468         [ +  + ]:       4606 :   for (const auto& p : m)
     469                 :            :   {
     470         [ +  - ]:       5008 :     Trace("cdcac") << "Isolating real roots of " << p << " over "
     471                 :       2504 :                    << d_assignment << std::endl;
     472                 :            : 
     473                 :       2504 :     auto tmp = isolateRealRoots(le, p);
     474                 :       2504 :     roots.insert(roots.end(), tmp.begin(), tmp.end());
     475                 :       2504 :   }
     476                 :       2102 :   roots.emplace_back(poly::Value::plus_infty());
     477                 :       2102 :   std::sort(roots.begin(), roots.end());
     478                 :            : 
     479                 :            :   // Now find the interval bounds
     480                 :       2102 :   poly::Value lower;
     481                 :       2102 :   poly::Value upper;
     482         [ +  - ]:       5173 :   for (std::size_t i = 0, n = roots.size(); i < n; ++i)
     483                 :            :   {
     484         [ +  + ]:       5173 :     if (sample < roots[i])
     485                 :            :     {
     486                 :       1544 :       lower = roots[i - 1];
     487                 :       1544 :       upper = roots[i];
     488                 :       1544 :       break;
     489                 :            :     }
     490         [ +  + ]:       3629 :     if (roots[i] == sample)
     491                 :            :     {
     492                 :        558 :       lower = sample;
     493                 :        558 :       upper = sample;
     494                 :        558 :       break;
     495                 :            :     }
     496                 :            :   }
     497 [ +  - ][ +  - ]:       2102 :   Assert(!is_none(lower) && !is_none(upper));
         [ -  + ][ -  + ]
                 [ -  - ]
     498                 :            : 
     499         [ +  + ]:       2102 :   if (!is_minus_infinity(lower))
     500                 :            :   {
     501                 :            :     // Identify polynomials that have a root at the lower bound
     502                 :       1359 :     d_assignment.set(d_variableOrdering[cur_variable], lower);
     503         [ +  + ]:       3109 :     for (const auto& p : m)
     504                 :            :     {
     505         [ +  - ]:       3500 :       Trace("cdcac") << "Evaluating " << p << " = 0 over " << d_assignment
     506                 :       1750 :                      << std::endl;
     507         [ +  + ]:       1750 :       if (evaluate_constraint(p, d_assignment, poly::SignCondition::EQ))
     508                 :            :       {
     509                 :       1359 :         l.add(p, true);
     510                 :            :       }
     511                 :            :     }
     512                 :       1359 :     d_assignment.unset(d_variableOrdering[cur_variable]);
     513                 :            :   }
     514         [ +  + ]:       2102 :   if (!is_plus_infinity(upper))
     515                 :            :   {
     516                 :            :     // Identify polynomials that have a root at the upper bound
     517                 :       1332 :     d_assignment.set(d_variableOrdering[cur_variable], upper);
     518         [ +  + ]:       3100 :     for (const auto& p : m)
     519                 :            :     {
     520         [ +  - ]:       3536 :       Trace("cdcac") << "Evaluating " << p << " = 0 over " << d_assignment
     521                 :       1768 :                      << std::endl;
     522         [ +  + ]:       1768 :       if (evaluate_constraint(p, d_assignment, poly::SignCondition::EQ))
     523                 :            :       {
     524                 :       1336 :         u.add(p, true);
     525                 :            :       }
     526                 :            :     }
     527                 :       1332 :     d_assignment.unset(d_variableOrdering[cur_variable]);
     528                 :            :   }
     529                 :            : 
     530         [ +  + ]:       2102 :   if (lower == upper)
     531                 :            :   {
     532                 :            :     // construct a point interval
     533                 :        558 :     return CACInterval{d_nextIntervalId++,
     534                 :            :                        poly::Interval(lower, false, upper, false),
     535                 :            :                        l,
     536                 :            :                        u,
     537                 :            :                        m,
     538                 :            :                        d,
     539                 :        558 :                        {}};
     540                 :            :   }
     541                 :            :   else
     542                 :            :   {
     543                 :            :     // construct an open interval
     544 [ -  + ][ -  + ]:       1544 :     Assert(lower < upper);
                 [ -  - ]
     545                 :       1544 :     return CACInterval{d_nextIntervalId++,
     546                 :            :                        poly::Interval(lower, true, upper, true),
     547                 :            :                        l,
     548                 :            :                        u,
     549                 :            :                        m,
     550                 :            :                        d,
     551                 :       1544 :                        {}};
     552                 :            :   }
     553                 :       2102 : }
     554                 :            : 
     555                 :       2665 : std::vector<CACInterval> CDCAC::getUnsatCoverImpl(std::size_t curVariable,
     556                 :            :                                                   bool returnFirstInterval)
     557                 :            : {
     558                 :       2665 :   d_env.getResourceManager()->spendResource(Resource::ArithNlCoveringStep);
     559         [ +  - ]:       5330 :   Trace("cdcac") << "Looking for unsat cover for "
     560                 :       2665 :                  << get_stream_variable(d_variableOrdering[curVariable])
     561                 :       2665 :                  << std::endl;
     562                 :       2665 :   std::vector<CACInterval> intervals = getUnsatIntervals(curVariable);
     563                 :            : 
     564         [ -  + ]:       2665 :   if (TraceIsOn("cdcac"))
     565                 :            :   {
     566         [ -  - ]:          0 :     Trace("cdcac") << "Unsat intervals for "
     567                 :          0 :                    << get_stream_variable(d_variableOrdering[curVariable])
     568                 :          0 :                    << ":" << std::endl;
     569         [ -  - ]:          0 :     for (const auto& i : intervals)
     570                 :            :     {
     571         [ -  - ]:          0 :       Trace("cdcac") << "-> " << i.d_interval << " from " << i.d_origins
     572                 :          0 :                      << std::endl;
     573                 :            :     }
     574                 :            :   }
     575                 :       2665 :   poly::Value sample;
     576                 :            : 
     577         [ +  + ]:       4853 :   while (sampleOutsideWithInitial(intervals, sample, curVariable))
     578                 :            :   {
     579         [ +  + ]:       2614 :     if (!checkIntegrality(curVariable, sample))
     580                 :            :     {
     581                 :            :       // the variable is integral, but the sample is not.
     582         [ +  - ]:        172 :       Trace("cdcac") << "Used " << sample << " for integer variable "
     583                 :         86 :                      << get_stream_variable(d_variableOrdering[curVariable])
     584                 :         86 :                      << std::endl;
     585                 :         86 :       auto newInterval = buildIntegralityInterval(curVariable, sample);
     586         [ +  - ]:        172 :       Trace("cdcac") << "Adding integrality interval " << newInterval.d_interval
     587                 :         86 :                      << std::endl;
     588                 :         86 :       intervals.emplace_back(newInterval);
     589                 :         86 :       pruneRedundantIntervals(intervals);
     590                 :         86 :       continue;
     591                 :         86 :     }
     592                 :       2528 :     d_assignment.set(d_variableOrdering[curVariable], sample);
     593         [ +  - ]:       2528 :     Trace("cdcac") << "Sample: " << d_assignment << std::endl;
     594         [ +  + ]:       2528 :     if (curVariable == d_variableOrdering.size() - 1)
     595                 :            :     {
     596                 :            :       // We have a full assignment. SAT!
     597         [ +  - ]:        142 :       Trace("cdcac") << "Found full assignment: " << d_assignment << std::endl;
     598                 :        426 :       return {};
     599                 :            :     }
     600         [ +  + ]:       2386 :     if (isProofEnabled())
     601                 :            :     {
     602                 :       1101 :       d_proof->startScope();
     603                 :       1101 :       d_proof->startRecursive();
     604                 :            :     }
     605                 :            :     // Recurse to next variable
     606                 :       2386 :     auto cov = getUnsatCoverImpl(curVariable + 1);
     607         [ +  + ]:       2386 :     if (cov.empty())
     608                 :            :     {
     609                 :            :       // Found SAT!
     610         [ +  - ]:        284 :       Trace("cdcac") << "SAT!" << std::endl;
     611                 :        284 :       return {};
     612                 :            :     }
     613         [ +  - ]:       2102 :     Trace("cdcac") << "Refuting Sample: " << d_assignment << std::endl;
     614                 :       2102 :     auto characterization = constructCharacterization(cov);
     615         [ +  - ]:       2102 :     Trace("cdcac") << "Characterization: " << characterization << std::endl;
     616                 :            : 
     617                 :       2102 :     d_assignment.unset(d_variableOrdering[curVariable]);
     618                 :            : 
     619         [ +  - ]:       2102 :     Trace("cdcac") << "Building interval..." << std::endl;
     620                 :            :     auto newInterval =
     621                 :       2102 :         intervalFromCharacterization(characterization, curVariable, sample);
     622         [ +  - ]:       2102 :     Trace("cdcac") << "New interval: " << newInterval.d_interval << std::endl;
     623                 :       2102 :     newInterval.d_origins = collectConstraints(cov);
     624                 :       2102 :     intervals.emplace_back(newInterval);
     625         [ +  + ]:       2102 :     if (isProofEnabled())
     626                 :            :     {
     627                 :       1087 :       d_proof->endRecursive(newInterval.d_id);
     628                 :            :       auto cell = d_proof->constructCell(
     629                 :       1087 :           d_constraints.varMapper()(d_variableOrdering[curVariable]),
     630                 :            :           newInterval,
     631                 :       1087 :           d_assignment,
     632                 :            :           sample,
     633                 :       2174 :           d_constraints.varMapper());
     634                 :       1087 :       d_proof->endScope(cell);
     635                 :       1087 :     }
     636                 :            : 
     637         [ -  + ]:       2102 :     if (returnFirstInterval)
     638                 :            :     {
     639                 :          0 :       return intervals;
     640                 :            :     }
     641                 :            : 
     642         [ +  - ]:       2102 :     Trace("cdcac") << "Added " << intervals.back().d_interval << std::endl;
     643         [ +  - ]:       4204 :     Trace("cdcac") << "\tlower:   " << intervals.back().d_lowerPolys
     644                 :       2102 :                    << std::endl;
     645         [ +  - ]:       4204 :     Trace("cdcac") << "\tupper:   " << intervals.back().d_upperPolys
     646                 :       2102 :                    << std::endl;
     647         [ +  - ]:       4204 :     Trace("cdcac") << "\tmain:    " << intervals.back().d_mainPolys
     648                 :       2102 :                    << std::endl;
     649         [ +  - ]:       4204 :     Trace("cdcac") << "\tdown:    " << intervals.back().d_downPolys
     650                 :       2102 :                    << std::endl;
     651         [ +  - ]:       2102 :     Trace("cdcac") << "\torigins: " << intervals.back().d_origins << std::endl;
     652                 :            : 
     653                 :            :     // Remove redundant intervals
     654                 :       2102 :     pruneRedundantIntervals(intervals);
     655 [ +  - ][ +  - ]:       2386 :   }
                 [ +  + ]
     656                 :            : 
     657         [ -  + ]:       2239 :   if (TraceIsOn("cdcac"))
     658                 :            :   {
     659         [ -  - ]:          0 :     Trace("cdcac") << "Returning intervals for "
     660                 :          0 :                    << get_stream_variable(d_variableOrdering[curVariable])
     661                 :          0 :                    << ":" << std::endl;
     662         [ -  - ]:          0 :     for (const auto& i : intervals)
     663                 :            :     {
     664         [ -  - ]:          0 :       Trace("cdcac") << "-> " << i.d_interval << std::endl;
     665                 :            :     }
     666                 :            :   }
     667                 :       2239 :   return intervals;
     668                 :       2665 : }
     669                 :            : 
     670                 :        279 : std::vector<CACInterval> CDCAC::getUnsatCover(bool returnFirstInterval)
     671                 :            : {
     672         [ +  + ]:        279 :   if (isProofEnabled())
     673                 :            :   {
     674                 :         71 :     d_proof->startRecursive();
     675                 :            :   }
     676                 :        279 :   auto res = getUnsatCoverImpl(0, returnFirstInterval);
     677         [ +  + ]:        279 :   if (isProofEnabled())
     678                 :            :   {
     679                 :         71 :     d_proof->endRecursive(0);
     680                 :            :   }
     681                 :        279 :   return res;
     682                 :          0 : }
     683                 :            : 
     684                 :        273 : void CDCAC::startNewProof()
     685                 :            : {
     686         [ +  + ]:        273 :   if (isProofEnabled())
     687                 :            :   {
     688                 :         71 :     d_proof->startNewProof();
     689                 :            :   }
     690                 :        273 : }
     691                 :            : 
     692                 :        136 : ProofGenerator* CDCAC::closeProof(const std::vector<Node>& assertions)
     693                 :            : {
     694         [ +  + ]:        136 :   if (isProofEnabled())
     695                 :            :   {
     696                 :         61 :     d_proof->endScope(assertions);
     697                 :         61 :     return d_proof->getProofGenerator();
     698                 :            :   }
     699                 :         75 :   return nullptr;
     700                 :            : }
     701                 :            : 
     702                 :       2614 : bool CDCAC::checkIntegrality(std::size_t cur_variable, const poly::Value& value)
     703                 :            : {
     704                 :       2614 :   Node var = d_constraints.varMapper()(d_variableOrdering[cur_variable]);
     705         [ +  + ]:       2614 :   if (!var.getType().isInteger())
     706                 :            :   {
     707                 :            :     // variable is not integral
     708                 :       2376 :     return true;
     709                 :            :   }
     710                 :        238 :   return poly::represents_integer(value);
     711                 :       2614 : }
     712                 :            : 
     713                 :         86 : CACInterval CDCAC::buildIntegralityInterval(std::size_t cur_variable,
     714                 :            :                                             const poly::Value& value)
     715                 :            : {
     716                 :         86 :   poly::Variable var = d_variableOrdering[cur_variable];
     717                 :         86 :   poly::Integer below = poly::floor(value);
     718                 :         86 :   poly::Integer above = poly::ceil(value);
     719                 :         86 :   const poly::Context& ctx = nodeManager()->getPolyContext();
     720                 :         86 :   poly::Polynomial pvar = poly::Polynomial(ctx, var);
     721                 :         86 :   poly::Polynomial pbelow = poly::Polynomial(ctx, below);
     722                 :         86 :   poly::Polynomial pabove = poly::Polynomial(ctx, above);
     723                 :            :   // construct var \in (below, above)
     724                 :         86 :   return CACInterval{d_nextIntervalId++,
     725                 :            :                      poly::Interval(below, above),
     726                 :            :                      {pvar - pbelow},
     727                 :            :                      {pvar - pabove},
     728                 :            :                      {pvar - pbelow, pvar - pabove},
     729                 :            :                      {},
     730                 :        516 :                      {}};
     731                 :         86 : }
     732                 :            : 
     733                 :        260 : bool CDCAC::hasRootAbove(const poly::Polynomial& p,
     734                 :            :                          const poly::Value& val) const
     735                 :            : {
     736                 :        260 :   auto roots = poly::isolate_real_roots(p, d_assignment);
     737                 :        260 :   return std::any_of(roots.begin(), roots.end(), [&val](const poly::Value& r) {
     738                 :        276 :     return r >= val;
     739                 :        520 :   });
     740                 :        260 : }
     741                 :            : 
     742                 :        234 : bool CDCAC::hasRootBelow(const poly::Polynomial& p,
     743                 :            :                          const poly::Value& val) const
     744                 :            : {
     745                 :        234 :   auto roots = poly::isolate_real_roots(p, d_assignment);
     746                 :        234 :   return std::any_of(roots.begin(), roots.end(), [&val](const poly::Value& r) {
     747                 :        234 :     return r <= val;
     748                 :        468 :   });
     749                 :        234 : }
     750                 :            : 
     751                 :       4853 : void CDCAC::pruneRedundantIntervals(std::vector<CACInterval>& intervals)
     752                 :            : {
     753                 :       4853 :   cleanIntervals(intervals);
     754         [ -  + ]:       4853 :   if (options().arith.nlCovPrune)
     755                 :            :   {
     756         [ -  - ]:          0 :     if (TraceIsOn("cdcac"))
     757                 :            :     {
     758                 :          0 :       auto copy = intervals;
     759                 :          0 :       removeRedundantIntervals(intervals);
     760         [ -  - ]:          0 :       if (copy.size() != intervals.size())
     761                 :            :       {
     762         [ -  - ]:          0 :         Trace("cdcac") << "Before pruning:";
     763                 :          0 :         for (const auto& i : copy) Trace("cdcac") << " " << i.d_interval;
     764         [ -  - ]:          0 :         Trace("cdcac") << std::endl;
     765         [ -  - ]:          0 :         Trace("cdcac") << "After pruning: ";
     766                 :          0 :         for (const auto& i : intervals) Trace("cdcac") << " " << i.d_interval;
     767         [ -  - ]:          0 :         Trace("cdcac") << std::endl;
     768                 :            :       }
     769                 :          0 :     }
     770                 :            :     else
     771                 :            :     {
     772                 :          0 :       removeRedundantIntervals(intervals);
     773                 :            :     }
     774                 :            :   }
     775         [ +  + ]:       4853 :   if (isProofEnabled())
     776                 :            :   {
     777                 :       2259 :     d_proof->pruneChildren([&intervals](std::size_t id) {
     778         [ -  + ]:       4627 :       if (id == 0) return false;
     779                 :       4627 :       return std::find_if(intervals.begin(),
     780                 :            :                           intervals.end(),
     781                 :       6901 :                           [id](const CACInterval& i) { return i.d_id == id; })
     782                 :       9254 :              == intervals.end();
     783                 :            :     });
     784                 :            :   }
     785                 :       4853 : }
     786                 :            : 
     787                 :       4767 : void CDCAC::prepareRootIsolation(LazardEvaluation& le,
     788                 :            :                                  size_t cur_variable) const
     789                 :            : {
     790         [ +  + ]:       4767 :   if (options().arith.nlCovLifting == options::nlCovLiftingMode::LAZARD)
     791                 :            :   {
     792         [ +  + ]:       1062 :     for (size_t vid = 0; vid < cur_variable; ++vid)
     793                 :            :     {
     794                 :        836 :       const auto& val = d_assignment.get(d_variableOrdering[vid]);
     795                 :        836 :       le.add(d_variableOrdering[vid], val);
     796                 :            :     }
     797                 :        226 :     le.addFreeVariable(d_variableOrdering[cur_variable]);
     798                 :            :   }
     799                 :       4767 : }
     800                 :            : 
     801                 :       2504 : std::vector<poly::Value> CDCAC::isolateRealRoots(
     802                 :            :     LazardEvaluation& le, const poly::Polynomial& p) const
     803                 :            : {
     804         [ +  + ]:       2504 :   if (options().arith.nlCovLifting == options::nlCovLiftingMode::LAZARD)
     805                 :            :   {
     806                 :        130 :     return le.isolateRealRoots(p);
     807                 :            :   }
     808                 :       2374 :   return poly::isolate_real_roots(p, d_assignment);
     809                 :            : }
     810                 :            : 
     811                 :            : }  // namespace coverings
     812                 :            : }  // namespace nl
     813                 :            : }  // namespace arith
     814                 :            : }  // namespace theory
     815                 :            : }  // namespace cvc5::internal
     816                 :            : 
     817                 :            : #endif

Generated by: LCOV version 1.14