LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/prop/cadical - cdclt_propagator.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 355 382 92.9 %
Date: 2026-08-10 10:35:18 Functions: 25 25 100.0 %
Branches: 214 386 55.4 %

           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                 :            :  * CDCL(T) IPASIRUP propagator for CaDiCaL.
      11                 :            :  */
      12                 :            : #include "prop/cadical/cdclt_propagator.h"
      13                 :            : 
      14                 :            : namespace cvc5::internal::prop::cadical {
      15                 :            : 
      16                 :         79 : CadicalPropagator::CadicalPropagator(prop::TheoryProxy* proxy,
      17                 :            :                                      context::Context* context,
      18                 :            :                                      CaDiCaL::Solver& solver,
      19                 :         79 :                                      StatisticsRegistry& stats)
      20                 :         79 :     : d_proxy(proxy), d_context(*context), d_solver(solver), d_stats(stats)
      21                 :            : {
      22                 :         79 :   d_var_info.emplace_back();  // 0: Not used
      23                 :         79 : }
      24                 :            : 
      25                 :      37978 : void CadicalPropagator::notify_assignment(const std::vector<int>& lits)
      26                 :            : {
      27 [ +  - ][ -  + ]:      37978 :   if (Trace("cadical::propagator").isConnected())
      28                 :            :   {
      29         [ -  - ]:          0 :     Trace("cadical::propagator") << "notif::assignments: { ";
      30         [ -  - ]:          0 :     for (auto lit : lits)
      31                 :            :     {
      32         [ -  - ]:          0 :       Trace("cadical::propagator") << lit << " ";
      33                 :            :     }
      34         [ -  - ]:          0 :     Trace("cadical::propagator") << "}" << std::endl;
      35                 :            :   }
      36                 :      37978 :   ++d_stats.notifyAssignment;
      37                 :            : 
      38         [ +  + ]:      37978 :   if (d_found_solution)
      39                 :            :   {
      40                 :         13 :     return;
      41                 :            :   }
      42                 :            : 
      43         [ +  + ]:      99879 :   for (const auto& lit : lits)
      44                 :            :   {
      45                 :      61914 :     SatLiteral slit = toSatLiteral(lit);
      46                 :      61914 :     SatVariable var = slit.getSatVariable();
      47 [ -  + ][ -  + ]:      61914 :     Assert(var < d_var_info.size());
                 [ -  - ]
      48                 :            : 
      49                 :      61914 :     auto& info = d_var_info[var];
      50                 :            : 
      51                 :            :     // Only consider active variables
      52         [ -  + ]:      61914 :     if (!info.is_active)
      53                 :            :     {
      54                 :          0 :       continue;
      55                 :            :     }
      56                 :            : 
      57                 :      61914 :     bool is_decision = d_solver.is_decision(lit);
      58                 :            : 
      59         [ +  - ]:     123828 :     Trace("cadical::propagator")
      60         [ -  - ]:          0 :         << "notif::assignment: [" << (is_decision ? "d" : "p") << "] " << slit
      61                 :          0 :         << " (level: " << d_decisions.size()
      62                 :          0 :         << ", level_intro: " << info.level_intro
      63                 :      61914 :         << ", level_user: " << current_user_level() << ")" << std::endl;
      64                 :            : 
      65                 :            :     // Save decision variables
      66         [ +  + ]:      61914 :     if (is_decision)
      67                 :            :     {
      68                 :      10268 :       d_decisions.back() = slit;
      69                 :            :     }
      70                 :            : 
      71 [ +  + ][ +  - ]:      61914 :     Assert(info.assignment == 0 || info.assignment == lit);
         [ -  + ][ -  + ]
                 [ -  - ]
      72                 :            : 
      73                 :            :     // Only notify theory proxy if variable was assigned a new value, not if
      74                 :            :     // it got fixed after assignment already happend.
      75         [ +  + ]:      61914 :     if (info.assignment == 0)
      76                 :            :     {
      77                 :      60697 :       info.assignment = lit;
      78                 :      60697 :       d_assignments.push_back(slit);
      79         [ +  + ]:      60697 :       if (info.is_theory_atom)
      80                 :            :       {
      81         [ +  - ]:      59486 :         Trace("cadical::propagator") << "enqueue: " << slit << std::endl;
      82         [ +  - ]:     118972 :         Trace("cadical::propagator")
      83 [ -  + ][ -  - ]:      59486 :             << "node:    " << d_proxy->getNode(slit) << std::endl;
      84                 :      59486 :         d_proxy->enqueueTheoryLiteral(slit);
      85                 :            :       }
      86                 :            :     }
      87                 :            :   }
      88                 :            : }
      89                 :            : 
      90                 :       1243 : void CadicalPropagator::notify_fixed_assignment(int lit)
      91                 :            : {
      92                 :       1243 :   SatLiteral slit = toSatLiteral(lit);
      93                 :       1243 :   SatVariable var = slit.getSatVariable();
      94                 :            : 
      95                 :            :   // We don't care about non-observed variables
      96         [ -  + ]:       1243 :   if (var >= d_var_info.size())
      97                 :            :   {
      98                 :         51 :     return;
      99                 :            :   }
     100                 :            : 
     101                 :       1243 :   auto& info = d_var_info[var];
     102                 :            :   // Only consider active variables
     103         [ +  + ]:       1243 :   if (!info.is_active)
     104                 :            :   {
     105                 :         51 :     return;
     106                 :            :   }
     107                 :       1192 :   ++d_stats.notifyFixedAssignment;
     108                 :            : 
     109         [ +  - ]:       2384 :   Trace("cadical::propagator")
     110                 :       1192 :       << "notif::fixed assignment: " << slit << std::endl;
     111                 :            : 
     112                 :            :   // Mark as fixed.
     113 [ -  + ][ -  + ]:       1192 :   Assert(!info.is_fixed);
                 [ -  - ]
     114                 :       1192 :   info.is_fixed = true;
     115                 :       1192 :   info.level_fixed = current_user_level();
     116                 :            :   // Trigger actual assignment.
     117                 :       1192 :   notify_assignment({lit});
     118                 :            : }
     119                 :            : 
     120                 :      17033 : void CadicalPropagator::notify_new_decision_level()
     121                 :            : {
     122                 :      17033 :   d_context.push();
     123                 :      17033 :   d_assignment_control.push_back(d_assignments.size());
     124                 :      17033 :   d_decisions.emplace_back();
     125         [ +  - ]:      34066 :   Trace("cadical::propagator")
     126                 :      17033 :       << "notif::decision: new level " << d_decisions.size() << std::endl;
     127                 :      17033 :   ++d_stats.notifyNewDecision;
     128                 :      17033 : }
     129                 :            : 
     130                 :      16196 : void CadicalPropagator::notify_backtrack(size_t level)
     131                 :            : {
     132         [ +  - ]:      16196 :   Trace("cadical::propagator") << "notif::backtrack: " << level << std::endl;
     133                 :            : 
     134                 :            :   // CaDiCaL may notify us about backtracks of decisions that we were not
     135                 :            :   // notified about. We can safely ignore them.
     136         [ +  + ]:      16196 :   if (d_decisions.size() <= level)
     137                 :            :   {
     138 [ -  + ][ -  + ]:         79 :     Assert(d_decisions.size() == 0);
                 [ -  - ]
     139                 :         79 :     return;
     140                 :            :   }
     141                 :      16117 :   d_found_solution = false;
     142                 :            : 
     143                 :            :   // Backtrack decisions
     144 [ -  + ][ -  + ]:      16117 :   Assert(d_decisions.size() > level);
                 [ -  - ]
     145 [ -  + ][ -  + ]:      16117 :   Assert(d_context.getLevel() > level);
                 [ -  - ]
     146         [ +  + ]:      33150 :   for (size_t cur_level = d_decisions.size(); cur_level > level; --cur_level)
     147                 :            :   {
     148                 :      17033 :     d_context.pop();
     149                 :      17033 :     d_decisions.pop_back();
     150                 :            :   }
     151                 :            : 
     152                 :            :   // Backtrack assignments. Genuine decision-level assignments are undone,
     153                 :            :   // but fixed assignments are permanent (decision level 0): we keep them
     154                 :            :   // assigned and retain them in d_assignments so that the user_pop()
     155                 :            :   // renotification machinery still sees them (it relies on d_assignments
     156                 :            :   // holding exactly the fixed literals once we are back at decision level 0).
     157                 :            :   // The corresponding theory literals are re-enqueued below, after notifying
     158                 :            :   // the theory proxy about the backtrack.
     159 [ -  + ][ -  + ]:      16117 :   Assert(!d_assignment_control.empty());
                 [ -  - ]
     160                 :      16117 :   size_t pop_to = d_assignment_control[level];
     161                 :      16117 :   d_assignment_control.resize(level);
     162                 :            : 
     163                 :      16117 :   size_t keep = pop_to;
     164                 :      16117 :   std::vector<SatLiteral> renotify;
     165         [ +  + ]:      75723 :   for (size_t i = pop_to, n = d_assignments.size(); i < n; ++i)
     166                 :            :   {
     167                 :      59606 :     SatLiteral lit = d_assignments[i];
     168                 :      59606 :     SatVariable var = lit.getSatVariable();
     169                 :      59606 :     auto& info = d_var_info[var];
     170         [ +  + ]:      59606 :     if (info.is_fixed)
     171                 :            :     {
     172                 :            :       // Permanent assignment: keep it and retain it in d_assignments.
     173         [ +  - ]:        101 :       Trace("cadical::propagator") << "keep fixed: " << var << std::endl;
     174                 :        101 :       d_assignments[keep++] = lit;
     175         [ +  + ]:        101 :       if (info.is_theory_atom)
     176                 :            :       {
     177                 :         65 :         renotify.push_back(lit);
     178                 :            :       }
     179                 :            :     }
     180                 :            :     else
     181                 :            :     {
     182         [ +  - ]:      59505 :       Trace("cadical::propagator") << "unassign: " << var << std::endl;
     183                 :      59505 :       info.assignment = 0;
     184                 :            :     }
     185                 :            :   }
     186                 :      16117 :   d_assignments.resize(keep);
     187                 :            : 
     188                 :            :   // Notify theory proxy about backtrack
     189                 :      16117 :   d_proxy->notifyBacktrack();
     190                 :            :   // Re-enqueue fixed theory literals that got backtracked at the theory level
     191                 :            :   // but remain assigned at the SAT level. This must happen after
     192                 :            :   // notifyBacktrack(), otherwise the theory backtrack would discard them.
     193         [ +  + ]:      16182 :   for (const SatLiteral& lit : renotify)
     194                 :            :   {
     195         [ +  - ]:        130 :     Trace("cadical::propagator")
     196                 :         65 :         << "re-enqueue (backtrack): " << lit << std::endl;
     197                 :         65 :     d_proxy->enqueueTheoryLiteral(lit);
     198                 :            :   }
     199                 :            :   // Clear the propagations since they are not valid anymore.
     200                 :      16117 :   d_propagations.clear();
     201                 :      16117 :   ++d_stats.notifyBacktrack;
     202                 :            : 
     203         [ +  - ]:      16117 :   Trace("cadical::propagator") << "notif::backtrack end" << std::endl;
     204                 :      16117 : }
     205                 :            : 
     206                 :        209 : bool CadicalPropagator::cb_check_found_model(
     207                 :            :     CVC5_UNUSED const std::vector<int>& model)
     208                 :            : {
     209         [ +  - ]:        209 :   Trace("cadical::propagator") << "cb::check_found_model" << std::endl;
     210                 :        209 :   bool recheck = false;
     211                 :            : 
     212         [ +  + ]:        209 :   if (d_found_solution)
     213                 :            :   {
     214                 :          7 :     return true;
     215                 :            :   }
     216                 :            : 
     217                 :        202 :   ++d_stats.cbCheckFoundModel;
     218                 :            :   // CaDiCaL may backtrack while importing clauses, which can result in some
     219                 :            :   // clauses not being processed. Make sure to add all clauses before
     220                 :            :   // checking the model.
     221         [ +  + ]:        202 :   if (!d_new_clauses.empty())
     222                 :            :   {
     223         [ +  - ]:         18 :     Trace("cadical::propagator") << "cb::check_found_model end: new "
     224                 :          0 :                                     "variables added via theory decision"
     225                 :          9 :                                  << std::endl;
     226                 :            :     // CaDiCaL expects us to be able to provide a reason for rejecting the
     227                 :            :     // model (it asserts that after this call, cb_has_external_clause()
     228                 :            :     // returns true). However, in this particular case, we want to force
     229                 :            :     // CaDiCaL to give us model values for the new variables that were
     230                 :            :     // introduced (to kick off the assignment notification machinery), we
     231                 :            :     // don't have a reason clause for rejecting the model. CaDiCaL's
     232                 :            :     // expectation will be weakened in the future to allow for this, but for
     233                 :            :     // now we simply add a tautology as reason to pacify CaDiCaL.
     234                 :          9 :     d_new_clauses.push_back(1);
     235                 :          9 :     d_new_clauses.push_back(-1);
     236                 :          9 :     d_new_clauses.push_back(0);
     237                 :          9 :     return false;
     238                 :            :   }
     239                 :            : 
     240                 :            :   // Check full model.
     241                 :            :   //
     242                 :            :   // First, we have to ensure that if the SAT solver determines sat without
     243                 :            :   // making any decisions, theory decisions are still requested until fixed
     244                 :            :   // point at least once since some modules, e.g., finite model finding, rely
     245                 :            :   // on this. Theory decisions may add new variables (while decisions
     246                 :            :   // requested by the decision engine will not). If new variables are added,
     247                 :            :   // we interrupt the check to force the SAT solver to extend the model with
     248                 :            :   // the new variables.
     249                 :        193 :   size_t size = d_var_info.size();
     250                 :            :   bool requirePhase, stopSearch;
     251                 :        193 :   d_proxy->getNextDecisionRequest(requirePhase, stopSearch);
     252         [ -  + ]:        193 :   if (d_var_info.size() != size)
     253                 :            :   {
     254         [ -  - ]:          0 :     Trace("cadical::propagator") << "cb::check_found_model end: new "
     255                 :          0 :                                     "variables added via theory decision"
     256                 :          0 :                                  << std::endl;
     257                 :          0 :     return false;
     258                 :            :   }
     259                 :            :   // Theory engine may trigger a recheck, unless new variables were added
     260                 :            :   // during check. If so, we break out of the check and have the SAT solver
     261                 :            :   // extend the model with the new variables.
     262                 :            :   do
     263                 :            :   {
     264         [ +  - ]:        404 :     Trace("cadical::propagator")
     265                 :        202 :         << "full check (recheck: " << recheck << ")" << std::endl;
     266                 :        202 :     d_proxy->theoryCheck(theory::Theory::Effort::EFFORT_FULL);
     267                 :        202 :     theory_propagate();
     268         [ +  + ]:        266 :     for (const SatLiteral& p : d_propagations)
     269                 :            :     {
     270         [ +  - ]:        128 :       Trace("cadical::propagator")
     271                 :         64 :           << "add propagation reason: " << p << std::endl;
     272                 :         64 :       SatClause clause;
     273                 :         64 :       d_proxy->explainPropagation(p, clause);
     274                 :         64 :       add_clause(clause);
     275                 :         64 :     }
     276                 :        202 :     d_propagations.clear();
     277                 :            : 
     278         [ +  + ]:        202 :     if (!d_new_clauses.empty())
     279                 :            :     {
     280                 :            :       // Will again call cb_check_found_model() after clauses were added.
     281                 :        166 :       recheck = false;
     282                 :            :     }
     283                 :            :     else
     284                 :            :     {
     285                 :         36 :       recheck = d_proxy->theoryNeedCheck();
     286                 :            :     }
     287 [ +  + ][ +  + ]:        202 :   } while (d_var_info.size() == size && recheck);
                 [ +  + ]
     288                 :            : 
     289         [ +  + ]:        193 :   if (d_var_info.size() != size)
     290                 :            :   {
     291         [ +  - ]:        282 :     Trace("cadical::propagator") << "cb::check_found_model end: new "
     292                 :          0 :                                     "variables added via theory check"
     293                 :        141 :                                  << std::endl;
     294                 :            :     // Same as above, until CaDiCaL's assertion that we have to have
     295                 :            :     // a reason clause for rejecting the model is weakened, we need to
     296                 :            :     // pacify it with a tautology.
     297                 :        141 :     d_new_clauses.push_back(1);
     298                 :        141 :     d_new_clauses.push_back(-1);
     299                 :        141 :     d_new_clauses.push_back(0);
     300                 :        141 :     return false;
     301                 :            :   }
     302                 :         52 :   bool res = done();
     303         [ +  - ]:        104 :   Trace("cadical::propagator")
     304                 :         52 :       << "cb::check_found_model end: done: " << res << std::endl;
     305                 :         52 :   return res;
     306                 :            : }
     307                 :            : 
     308                 :      16392 : int CadicalPropagator::cb_decide()
     309                 :            : {
     310         [ +  - ]:      16392 :   Trace("cadical::propagator") << "cb::decide" << std::endl;
     311         [ +  + ]:      16392 :   if (d_found_solution)
     312                 :            :   {
     313                 :          6 :     return 0;
     314                 :            :   }
     315                 :      16386 :   ++d_stats.cbDecide;
     316                 :      16386 :   bool stopSearch = false;
     317                 :      16386 :   bool requirePhase = false;
     318                 :      16386 :   SatLiteral lit = d_proxy->getNextDecisionRequest(requirePhase, stopSearch);
     319                 :            :   // We found a partial model, let's check it.
     320         [ +  + ]:      16386 :   if (stopSearch)
     321                 :            :   {
     322                 :         92 :     d_found_solution = cb_check_found_model({});
     323         [ +  + ]:         92 :     if (d_found_solution)
     324                 :            :     {
     325         [ +  - ]:          7 :       Trace("cadical::propagator") << "Found solution" << std::endl;
     326                 :          7 :       d_found_solution = d_proxy->isDecisionEngineDone();
     327         [ -  + ]:          7 :       if (!d_found_solution)
     328                 :            :       {
     329         [ -  - ]:          0 :         Trace("cadical::propagator") << "Decision engine not done" << std::endl;
     330                 :          0 :         lit = d_proxy->getNextDecisionRequest(requirePhase, stopSearch);
     331                 :            :       }
     332                 :            :     }
     333                 :            :     else
     334                 :            :     {
     335         [ +  - ]:         85 :       Trace("cadical::propagator") << "No solution found yet" << std::endl;
     336                 :            :     }
     337                 :            :   }
     338 [ +  + ][ +  + ]:      16386 :   if (!stopSearch && lit != undefSatLiteral)
                 [ +  + ]
     339                 :            :   {
     340         [ +  + ]:      16193 :     if (!requirePhase)
     341                 :            :     {
     342                 :      16126 :       int8_t phase = d_var_info[lit.getSatVariable()].phase;
     343         [ -  + ]:      16126 :       if (phase != 0)
     344                 :            :       {
     345         [ -  - ]:          0 :         if ((phase == -1 && !lit.isNegated())
     346 [ -  - ][ -  - ]:          0 :             || (phase == 1 && lit.isNegated()))
         [ -  - ][ -  - ]
     347                 :            :         {
     348                 :          0 :           lit = ~lit;
     349                 :            :         }
     350                 :            :       }
     351                 :            :     }
     352         [ +  - ]:      16193 :     Trace("cadical::propagator") << "cb::decide: " << lit << std::endl;
     353                 :      16193 :     return toCadicalLit(lit);
     354                 :            :   }
     355         [ +  - ]:        193 :   Trace("cadical::propagator") << "cb::decide: 0" << std::endl;
     356                 :        193 :   return 0;
     357                 :            : }
     358                 :            : 
     359                 :      66293 : int CadicalPropagator::cb_propagate()
     360                 :            : {
     361         [ +  + ]:      66293 :   if (d_found_solution)
     362                 :            :   {
     363                 :         13 :     return 0;
     364                 :            :   }
     365                 :      66280 :   ++d_stats.cbPropagate;
     366         [ +  - ]:      66280 :   Trace("cadical::propagator") << "cb::propagate" << std::endl;
     367         [ +  + ]:      66280 :   if (d_propagations.empty())
     368                 :            :   {
     369                 :            :     // Only propagate if all activation literals are processed. Activation
     370                 :            :     // literals are always assumed first. If we don't do this, explanations
     371                 :            :     // for theory propagations may force activation literals to different
     372                 :            :     // values before they can get decided on.
     373         [ +  + ]:      36399 :     if (d_decisions.size() < current_user_level())
     374                 :            :     {
     375                 :         42 :       return 0;
     376                 :            :     }
     377                 :      36357 :     d_proxy->theoryCheck(theory::Theory::Effort::EFFORT_STANDARD);
     378                 :      36357 :     theory_propagate();
     379                 :            :   }
     380                 :      66238 :   return next_propagation();
     381                 :            : }
     382                 :            : 
     383                 :      21526 : int CadicalPropagator::cb_add_reason_clause_lit(int propagated_lit)
     384                 :            : {
     385                 :      21526 :   ++d_stats.cbAddReasonClauseLit;
     386                 :            :   // Get reason for propagated_lit.
     387         [ +  + ]:      21526 :   if (!d_processing_reason)
     388                 :            :   {
     389 [ -  + ][ -  + ]:       2209 :     Assert(d_reason.empty());
                 [ -  - ]
     390                 :       2209 :     SatLiteral slit = toSatLiteral(propagated_lit);
     391                 :       2209 :     SatClause clause;
     392                 :       2209 :     d_proxy->explainPropagation(slit, clause);
     393 [ -  + ][ -  + ]:       2209 :     Assert(d_in_search);
                 [ -  - ]
     394                 :            :     // Add activation literal of the clause's user level to the reason.
     395                 :       2209 :     SatLiteral alit = activation_lit(clause_user_level(clause));
     396         [ -  + ]:       2209 :     if (alit != undefSatLiteral)
     397                 :            :     {
     398                 :          0 :       d_reason.push_back(alit);
     399                 :            :     }
     400                 :       2209 :     d_reason.insert(d_reason.end(), clause.begin(), clause.end());
     401                 :       2209 :     d_processing_reason = true;
     402         [ +  - ]:       4418 :     Trace("cadical::propagator")
     403                 :       2209 :         << "cb::reason: " << slit << ", size: " << d_reason.size() << std::endl;
     404                 :       2209 :   }
     405                 :            : 
     406                 :            :   // We are done processing the reason for propagated_lit.
     407         [ +  + ]:      21526 :   if (d_reason.empty())
     408                 :            :   {
     409                 :       2209 :     d_processing_reason = false;
     410                 :       2209 :     return 0;
     411                 :            :   }
     412                 :            : 
     413                 :            :   // Return next literal of the reason for propagated_lit.
     414         [ +  - ]:      38634 :   Trace("cadical::propagator") << "cb::reason: " << toSatLiteral(propagated_lit)
     415                 :      19317 :                                << " " << d_reason.front() << std::endl;
     416                 :      19317 :   int lit = toCadicalLit(d_reason.front());
     417                 :      19317 :   d_reason.pop_front();
     418                 :      19317 :   return lit;
     419                 :            : }
     420                 :            : 
     421                 :      19369 : bool CadicalPropagator::cb_has_external_clause(bool& is_forgettable)
     422                 :            : {
     423                 :      19369 :   ++d_stats.cbHasExternalClause;
     424                 :      19369 :   is_forgettable = false;
     425                 :      19369 :   return !d_new_clauses.empty();
     426                 :            : }
     427                 :            : 
     428                 :       7323 : int CadicalPropagator::cb_add_external_clause_lit()
     429                 :            : {
     430                 :       7323 :   ++d_stats.cbAddExternalClauseLit;
     431 [ -  + ][ -  + ]:       7323 :   Assert(!d_new_clauses.empty());
                 [ -  - ]
     432                 :       7323 :   CadicalLit lit = d_new_clauses.front();
     433                 :       7323 :   d_new_clauses.pop_front();
     434         [ +  - ]:      14646 :   Trace("cadical::propagator")
     435                 :       7323 :       << "external_clause: " << toSatLiteral(lit) << std::endl;
     436                 :       7323 :   return lit;
     437                 :            : }
     438                 :            : 
     439                 :     130309 : SatValue CadicalPropagator::value(SatLiteral lit) const
     440                 :            : {
     441                 :     130309 :   SatVariable var = lit.getSatVariable();
     442                 :     130309 :   SatValue val = SAT_VALUE_UNKNOWN;
     443                 :     130309 :   int32_t assign = d_var_info[var].assignment;
     444         [ +  + ]:     130309 :   if (assign != 0)
     445                 :            :   {
     446         [ +  + ]:      75563 :     val = toSatValueLit(lit.isNegated() ? -assign : assign);
     447                 :            :   }
     448         [ +  - ]:     130309 :   Trace("cadical::propagator") << "value: " << lit << ": " << val << std::endl;
     449                 :     130309 :   return val;
     450                 :            : }
     451                 :            : 
     452                 :       2657 : void CadicalPropagator::add_clause(const SatClause& clause)
     453                 :            : {
     454                 :       2657 :   std::vector<CadicalLit> lits;
     455                 :            :   // Note: Removable clauses can be added to lower user levels to avoid
     456                 :            :   //       deleting them too eagerly. For example, conflicts may be learned
     457                 :            :   //       at a user level N even though it only has literals of at most user
     458                 :            :   //       level N - 2. In this case we can add the clause at N - 2 instead
     459                 :            :   //       of deleting the clause when popping user level N, which would
     460                 :            :   //       require us to relearn the clause again.
     461         [ +  + ]:       2657 :   uint32_t max_user_level = d_in_search ? 0 : current_user_level();
     462         [ +  + ]:       9370 :   for (const SatLiteral& lit : clause)
     463                 :            :   {
     464                 :       7045 :     SatVariable var = lit.getSatVariable();
     465 [ -  + ][ -  + ]:       7045 :     Assert(var < d_var_info.size());
                 [ -  - ]
     466                 :       7045 :     const auto& info = d_var_info[var];
     467 [ -  + ][ -  + ]:       7045 :     Assert(info.is_active);
                 [ -  - ]
     468         [ +  + ]:       7045 :     if (info.is_fixed)
     469                 :            :     {
     470         [ +  + ]:       1296 :       int32_t val = lit.isNegated() ? -info.assignment : info.assignment;
     471 [ -  + ][ -  + ]:       1296 :       Assert(val != 0);
                 [ -  - ]
     472         [ +  + ]:       1296 :       if (val > 0)
     473                 :            :       {
     474                 :            :         // Clause satisfied by fixed literal, no clause added
     475                 :        332 :         return;
     476                 :            :       }
     477                 :            :     }
     478                 :       6713 :     max_user_level = std::max(max_user_level, info.level_intro);
     479                 :       6713 :     lits.push_back(toCadicalLit(lit));
     480                 :            :   }
     481         [ +  - ]:       2325 :   if (!lits.empty())
     482                 :            :   {
     483                 :            :     // Determine activation literal based on max user level of clause.
     484                 :       2325 :     SatLiteral alit = activation_lit(max_user_level);
     485         [ +  + ]:       2325 :     if (alit != undefSatLiteral)
     486                 :            :     {
     487                 :         49 :       lits.insert(lits.begin(), toCadicalLit(alit));
     488                 :            :     }
     489                 :            :     // Do not immediately add clauses added during search. We have to buffer
     490                 :            :     // them and add them during the cb_add_reason_clause_lit callback.
     491         [ +  + ]:       2325 :     if (d_in_search)
     492                 :            :     {
     493                 :       1595 :       d_new_clauses.insert(d_new_clauses.end(), lits.begin(), lits.end());
     494                 :       1595 :       d_new_clauses.push_back(0);
     495                 :            :     }
     496                 :            :     else
     497                 :            :     {
     498         [ +  + ]:       2174 :       for (const auto& lit : lits)
     499                 :            :       {
     500                 :       1444 :         d_solver.add(lit);
     501                 :            :       }
     502                 :        730 :       d_solver.add(0);
     503                 :            :     }
     504                 :            :   }
     505                 :            :   // // Add empty clause
     506                 :            :   // else if (num_false == clause.size())
     507                 :            :   // {
     508                 :            :   //   d_solver.add(0);
     509                 :            :   // }
     510         [ +  + ]:       2657 : }
     511                 :            : 
     512                 :       2109 : void CadicalPropagator::add_new_var(const SatVariable& var, bool is_theory_atom)
     513                 :            : {
     514                 :            :   // Since activation literals are not tracked here, we have to make sure to
     515                 :            :   // properly resize d_var_info.
     516         [ -  + ]:       2109 :   if (var > d_var_info.size())
     517                 :            :   {
     518                 :          0 :     d_var_info.resize(var);
     519                 :            :   }
     520 [ -  + ][ -  + ]:       2109 :   Assert(d_var_info.size() == var);
                 [ -  - ]
     521                 :            : 
     522                 :            :   // Boolean variables are not theory atoms, but may still occur in
     523                 :            :   // lemmas/conflicts sent to the SAT solver. Hence, we have to observe them
     524                 :            :   // since CaDiCaL expects all literals sent back to be observed.
     525                 :       2109 :   d_solver.add_observed_var(toCadicalVar(var));
     526                 :       2109 :   d_active_vars.push_back(var);
     527         [ +  - ]:       4218 :   Trace("cadical::propagator")
     528                 :       2109 :       << "new var: " << var << " (level: " << current_user_level()
     529                 :          0 :       << ", is_theory_atom: " << is_theory_atom
     530                 :       2109 :       << ", in_search: " << d_in_search << ")" << std::endl;
     531                 :       2109 :   auto& info = d_var_info.emplace_back();
     532                 :       2109 :   info.level_intro = current_user_level();
     533                 :       2109 :   info.is_theory_atom = is_theory_atom;
     534                 :       2109 : }
     535                 :            : 
     536                 :         79 : bool CadicalPropagator::done() const
     537                 :            : {
     538         [ +  + ]:         79 :   if (!d_new_clauses.empty())
     539                 :            :   {
     540         [ +  - ]:         25 :     Trace("cadical::propagator") << "not done: pending clauses" << std::endl;
     541                 :         25 :     return false;
     542                 :            :   }
     543         [ -  + ]:         54 :   if (d_proxy->theoryNeedCheck())
     544                 :            :   {
     545         [ -  - ]:          0 :     Trace("cadical::propagator") << "not done: theory need check" << std::endl;
     546                 :          0 :     return false;
     547                 :            :   }
     548         [ +  + ]:         54 :   if (d_found_solution)
     549                 :            :   {
     550         [ +  - ]:          7 :     Trace("cadical::propagator") << "done: found partial solution" << std::endl;
     551                 :            :   }
     552                 :            :   else
     553                 :            :   {
     554         [ +  - ]:         94 :     Trace("cadical::propagator")
     555                 :         47 :         << "done: full assignment consistent" << std::endl;
     556                 :            :   }
     557                 :         54 :   return true;
     558                 :            : }
     559                 :            : 
     560                 :         46 : void CadicalPropagator::user_push()
     561                 :            : {
     562         [ +  - ]:         46 :   Trace("cadical::propagator") << "user push: " << d_active_vars_control.size();
     563                 :         46 :   d_active_vars_control.push_back(d_active_vars.size());
     564         [ +  - ]:         92 :   Trace("cadical::propagator")
     565                 :         46 :       << " -> " << d_active_vars_control.size() << std::endl;
     566                 :         46 : }
     567                 :            : 
     568                 :         46 : void CadicalPropagator::set_activation_lit(SatVariable alit)
     569                 :            : {
     570                 :         46 :   d_activation_literals.push_back(SatLiteral(alit));
     571         [ +  - ]:         92 :   Trace("cadical::propagator")
     572                 :         46 :       << "enable activation lit: " << alit << std::endl;
     573                 :         46 : }
     574                 :            : 
     575                 :         32 : void CadicalPropagator::user_pop()
     576                 :            : {
     577         [ +  - ]:         32 :   Trace("cadical::propagator") << "user pop: " << d_active_vars_control.size();
     578                 :         32 :   size_t pop_to = d_active_vars_control.back();
     579                 :         32 :   d_active_vars_control.pop_back();
     580         [ +  - ]:         64 :   Trace("cadical::propagator")
     581                 :         32 :       << " -> " << d_active_vars_control.size() << std::endl;
     582                 :            : 
     583                 :            :   // Disable activation literal for popped user level. The activation literal
     584                 :            :   // will be set inactive below and is added as unit clause, which will satisfy
     585                 :            :   // all clauses added in this user level and get garbage collected in CaDiCal.
     586                 :         32 :   SatLiteral alit = current_activation_lit();
     587         [ +  - ]:         64 :   Trace("cadical::propagator")
     588                 :         32 :       << "disable activation lit: " << alit << std::endl;
     589                 :         32 :   d_activation_literals.pop_back();
     590                 :            : 
     591                 :         32 :   size_t user_level = current_user_level();
     592                 :            : 
     593                 :            :   // Unregister popped variables so that CaDiCaL does not notify us anymore
     594                 :            :   // about assignments.
     595 [ -  + ][ -  + ]:         32 :   Assert(pop_to <= d_active_vars.size());
                 [ -  - ]
     596                 :         32 :   std::vector<SatVariable> fixed;
     597         [ +  + ]:         93 :   while (d_active_vars.size() > pop_to)
     598                 :            :   {
     599                 :         61 :     SatVariable var = d_active_vars.back();
     600                 :         61 :     const auto& info = d_var_info[var];
     601                 :         61 :     d_active_vars.pop_back();
     602                 :            : 
     603                 :            :     // We keep fixed literals that correspond to theory atoms introduced in
     604                 :            :     // lower user levels, since we have to renotify them before the next
     605                 :            :     // solve call.
     606 [ +  + ][ -  + ]:         61 :     if (info.is_fixed && info.is_theory_atom && info.level_intro <= user_level)
                 [ -  - ]
     607                 :            :     {
     608                 :          0 :       fixed.push_back(var);
     609                 :            :     }
     610                 :            :     else
     611                 :            :     {
     612         [ +  - ]:         61 :       Trace("cadical::propagator") << "set inactive: " << var << std::endl;
     613                 :         61 :       d_var_info[var].is_active = false;
     614                 :         61 :       d_solver.remove_observed_var(toCadicalVar(var));
     615 [ -  + ][ -  + ]:         61 :       Assert(info.level_intro > user_level);
                 [ -  - ]
     616                 :            :       // Fix value of inactive variables in order to avoid CaDiCaL from
     617                 :            :       // deciding on them again. This make a huge difference in performance
     618                 :            :       // for incremental problems with many check-sat calls.
     619                 :         61 :       d_solver.add(toCadicalVar(var));
     620                 :         61 :       d_solver.add(0);
     621                 :            :     }
     622                 :            :   }
     623                 :            :   // Re-add fixed active vars in the order they were added to d_active_vars.
     624                 :         32 :   d_active_vars.insert(d_active_vars.end(), fixed.rbegin(), fixed.rend());
     625                 :            : 
     626                 :            :   // We are at decicion level 0 at this point.
     627 [ -  + ][ -  + ]:         32 :   Assert(d_decisions.empty());
                 [ -  - ]
     628 [ -  + ][ -  + ]:         32 :   Assert(d_assignment_control.empty());
                 [ -  - ]
     629                 :            :   // At this point, only fixed literals will be on d_assignments, now we have
     630                 :            :   // to determine which of these are still relevant in the current user
     631                 :            :   // level. If the variable is still active here, it means that it is still
     632                 :            :   // relevant for the current user level. If its assignment was fixed in a
     633                 :            :   // higher user level, we have to renotify the fixed literal in the current
     634                 :            :   // level (or in the user level of the next solve call). This happens by
     635                 :            :   // pushing the literal onto the d_renotify_fixed vector.
     636                 :         32 :   auto it = d_assignments.begin();
     637         [ +  + ]:        174 :   while (it != d_assignments.end())
     638                 :            :   {
     639                 :        142 :     SatLiteral lit = *it;
     640                 :        142 :     auto& info = d_var_info[lit.getSatVariable()];
     641 [ -  + ][ -  + ]:        142 :     Assert(info.is_fixed);
                 [ -  - ]
     642                 :            : 
     643                 :            :     // Remove inactive variables from the assignment vector.
     644         [ +  + ]:        142 :     if (!info.is_active)
     645                 :            :     {
     646                 :          8 :       it = d_assignments.erase(it);
     647                 :          8 :       continue;
     648                 :            :     }
     649                 :            : 
     650                 :            :     // Renotify fixed literals if it was fixed in a higher user level.
     651 [ +  + ][ +  + ]:        134 :     if (info.is_theory_atom && info.level_fixed > user_level)
     652                 :            :     {
     653         [ +  - ]:         32 :       Trace("cadical::propagator")
     654                 :          0 :           << "queue renotify: " << lit << " (level_intro: " << info.level_intro
     655                 :         16 :           << ", level_fixed: " << info.level_fixed << ")" << std::endl;
     656                 :         16 :       d_renotify_fixed.push_back(lit);
     657                 :            :     }
     658                 :        134 :     ++it;
     659                 :            :   }
     660                 :         32 : }
     661                 :            : 
     662                 :         61 : void CadicalPropagator::phase(SatLiteral lit)
     663                 :            : {
     664                 :         61 :   d_solver.phase(toCadicalLit(lit));
     665         [ -  + ]:         61 :   d_var_info[lit.getSatVariable()].phase = lit.isNegated() ? -1 : 1;
     666                 :         61 : }
     667                 :            : 
     668                 :         34 : const SatLiteral& CadicalPropagator::current_activation_lit() const
     669                 :            : {
     670         [ -  + ]:         34 :   if (d_activation_literals.empty())
     671                 :            :   {
     672                 :          0 :     return undefSatLiteral;
     673                 :            :   }
     674                 :         34 :   return d_activation_literals.back();
     675                 :            : }
     676                 :            : 
     677                 :       4541 : const SatLiteral& CadicalPropagator::activation_lit(size_t user_level) const
     678                 :            : {
     679                 :            :   // User level 0 has no activation literal.
     680         [ +  + ]:       4541 :   if (user_level == 0)
     681                 :            :   {
     682                 :       4487 :     return undefSatLiteral;
     683                 :            :   }
     684 [ -  + ][ -  + ]:         54 :   Assert(user_level <= d_activation_literals.size());
                 [ -  - ]
     685                 :         54 :   return d_activation_literals[user_level - 1];
     686                 :            : }
     687                 :            : 
     688                 :       2215 : uint32_t CadicalPropagator::clause_user_level(const SatClause& clause) const
     689                 :            : {
     690                 :       2215 :   uint32_t max_user_level = 0;
     691         [ +  + ]:      21545 :   for (const SatLiteral& lit : clause)
     692                 :            :   {
     693                 :      19330 :     SatVariable var = lit.getSatVariable();
     694 [ -  + ][ -  + ]:      19330 :     Assert(var < d_var_info.size());
                 [ -  - ]
     695                 :      19330 :     max_user_level = std::max(max_user_level, d_var_info[var].level_intro);
     696                 :            :   }
     697                 :       2215 :   return max_user_level;
     698                 :            : }
     699                 :            : 
     700                 :        101 : void CadicalPropagator::renotify_fixed()
     701                 :            : {
     702                 :        101 :   ++d_stats.renotifyFixed;
     703         [ +  + ]:        117 :   for (const auto& lit : d_renotify_fixed)
     704                 :            :   {
     705         [ +  - ]:         32 :     Trace("cadical::propagator")
     706                 :         16 :         << "re-enqueue (user pop): " << lit << std::endl;
     707                 :            :     // Re-enqueue fixed theory literal
     708                 :         16 :     d_proxy->enqueueTheoryLiteral(lit);
     709                 :            :     // We are notifying fixed literals at the current user level, update the
     710                 :            :     // level at which the variable was fixed, so that it will be renotified,
     711                 :            :     // if needed in lower user levels.
     712                 :         16 :     d_var_info[lit.getSatVariable()].level_fixed = current_user_level();
     713                 :         16 :     ++d_stats.renotifyFixedLit;
     714                 :            :   }
     715                 :        101 :   d_renotify_fixed.clear();
     716                 :        101 : }
     717                 :            : 
     718                 :      36559 : void CadicalPropagator::theory_propagate()
     719                 :            : {
     720                 :      36559 :   SatClause propagated_lits;
     721                 :      36559 :   d_proxy->theoryPropagate(propagated_lits);
     722         [ +  - ]:      73118 :   Trace("cadical::propagator")
     723                 :      36559 :       << "new propagations: " << propagated_lits.size() << std::endl;
     724                 :            : 
     725         [ +  + ]:      85778 :   for (const auto& lit : propagated_lits)
     726                 :            :   {
     727         [ +  - ]:      49219 :     Trace("cadical::propagator") << "new propagation: " << lit << std::endl;
     728                 :      49219 :     d_propagations.push_back(lit);
     729                 :            :   }
     730                 :      36559 : }
     731                 :            : 
     732                 :      66238 : int CadicalPropagator::next_propagation()
     733                 :            : {
     734         [ +  + ]:      66238 :   if (d_propagations.empty())
     735                 :            :   {
     736                 :      17657 :     return 0;
     737                 :            :   }
     738                 :      48581 :   SatLiteral next = d_propagations.front();
     739                 :      48581 :   d_propagations.pop_front();
     740         [ +  - ]:      48581 :   Trace("cadical::propagator") << "propagate: " << next << std::endl;
     741                 :      48581 :   return toCadicalLit(next);
     742                 :            : }
     743                 :            : 
     744                 :            : }  // namespace cvc5::internal::prop::cadical

Generated by: LCOV version 1.14