LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/theory/arith - branch_and_bound.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 62 69 89.9 %
Date: 2026-02-09 12:26:33 Functions: 3 3 100.0 %
Branches: 21 42 50.0 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Andrew Reynolds, Aina Niemetz, Hans-Joerg Schurr
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Branch and bound for arithmetic
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "theory/arith/branch_and_bound.h"
      17                 :            : 
      18                 :            : #include "expr/skolem_manager.h"
      19                 :            : #include "options/arith_options.h"
      20                 :            : #include "proof/eager_proof_generator.h"
      21                 :            : #include "proof/proof_node.h"
      22                 :            : #include "theory/arith/arith_utilities.h"
      23                 :            : #include "theory/rewriter.h"
      24                 :            : #include "theory/theory.h"
      25                 :            : 
      26                 :            : using namespace cvc5::internal::kind;
      27                 :            : 
      28                 :            : namespace cvc5::internal {
      29                 :            : namespace theory {
      30                 :            : namespace arith {
      31                 :            : 
      32                 :      50340 : BranchAndBound::BranchAndBound(Env& env,
      33                 :            :                                TheoryState& s,
      34                 :            :                                InferenceManager& im,
      35                 :      50340 :                                PreprocessRewriteEq& ppre)
      36                 :            :     : EnvObj(env),
      37                 :            :       d_astate(s),
      38                 :            :       d_im(im),
      39                 :            :       d_ppre(ppre),
      40                 :      50340 :       d_pfGen(new EagerProofGenerator(env, userContext()))
      41                 :            : {
      42                 :      50340 : }
      43                 :            : 
      44                 :       3220 : std::vector<TrustNode> BranchAndBound::branchIntegerVariable(TNode var,
      45                 :            :                                                              Rational value)
      46                 :            : {
      47                 :       3220 :   std::vector<TrustNode> lems;
      48                 :       3220 :   NodeManager* nm = nodeManager();
      49                 :       6440 :   Integer floor = value.floor();
      50         [ +  + ]:       3220 :   if (options().arith.brabTest)
      51                 :            :   {
      52         [ +  - ]:       3216 :     Trace("integers") << "branch-round-and-bound enabled" << std::endl;
      53                 :       6432 :     Integer ceil = value.ceiling();
      54                 :       6432 :     Rational f = value - floor;
      55                 :            :     // Multiply by -1 to get abs value.
      56                 :       9648 :     Rational c = (value - ceil) * (-1);
      57         [ +  + ]:       6432 :     Integer nearest = (c > f) ? floor : ceil;
      58                 :            : 
      59                 :            :     // Prioritize trying a simple rounding of the real solution first,
      60                 :            :     // it that fails, fall back on original branch and bound strategy.
      61                 :       9648 :     Node ub = rewrite(nm->mkNode(Kind::LEQ, var, nm->mkConstInt(nearest - 1)));
      62                 :            :     // The rewritten form should be a GEQ literal, otherwise the split returned
      63                 :            :     // by this method will not have its intended effect
      64                 :       6432 :     Assert(ub.getKind() == Kind::GEQ
      65                 :            :            || (ub.getKind() == Kind::NOT && ub[0].getKind() == Kind::GEQ));
      66         [ +  - ]:       6432 :     Node ubatom = ub.getKind() == Kind::NOT ? ub[0] : ub;
      67                 :       9648 :     Node lb = rewrite(nm->mkNode(Kind::GEQ, var, nm->mkConstInt(nearest + 1)));
      68                 :       9648 :     Node right = nm->mkNode(Kind::OR, ub, lb);
      69                 :       9648 :     Node rawEq = nm->mkNode(Kind::EQUAL, var, nm->mkConstInt(nearest));
      70                 :       6432 :     Node eq = rewrite(rawEq);
      71                 :            :     // Also preprocess it before we send it out. This is important since
      72                 :            :     // arithmetic may prefer eliminating equalities.
      73                 :       6432 :     TrustNode teq;
      74         [ +  + ]:       3216 :     if (d_env.theoryOf(eq) == THEORY_ARITH)
      75                 :            :     {
      76                 :       3007 :       teq = d_ppre.ppRewriteEq(eq);
      77         [ +  + ]:       3007 :       eq = teq.isNull() ? eq : teq.getNode();
      78                 :            :     }
      79                 :       6432 :     Node literal = d_astate.getValuation().ensureLiteral(eq);
      80         [ +  - ]:       3216 :     Trace("integers") << "eq: " << eq << "\nto: " << literal << std::endl;
      81                 :       3216 :     d_im.preferPhase(literal, true);
      82                 :       9648 :     Node l = nm->mkNode(Kind::OR, literal, right);
      83         [ +  - ]:       3216 :     Trace("integers") << "l: " << l << std::endl;
      84         [ +  + ]:       3216 :     if (proofsEnabled())
      85                 :            :     {
      86                 :       1267 :       ProofNodeManager* pnm = d_env.getProofNodeManager();
      87                 :       3801 :       Node less = nm->mkNode(Kind::LT, var, nm->mkConstInt(nearest));
      88                 :       3801 :       Node greater = nm->mkNode(Kind::GT, var, nm->mkConstInt(nearest));
      89                 :            :       // TODO (project #37): justify. Thread proofs through *ensureLiteral*.
      90         [ +  - ]:       1267 :       Trace("integers::pf") << "less: " << less << std::endl;
      91         [ +  - ]:       1267 :       Trace("integers::pf") << "greater: " << greater << std::endl;
      92         [ +  - ]:       1267 :       Trace("integers::pf") << "literal: " << literal << std::endl;
      93         [ +  - ]:       1267 :       Trace("integers::pf") << "eq: " << eq << std::endl;
      94         [ +  - ]:       1267 :       Trace("integers::pf") << "rawEq: " << rawEq << std::endl;
      95                 :       2534 :       Pf pfNotLit = pnm->mkAssume(literal.negate());
      96                 :            :       // rewrite notLiteral to notRawEq, using teq.
      97                 :            :       Pf pfNotRawEq =
      98                 :       1267 :           literal == rawEq
      99                 :            :               ? pfNotLit
     100                 :            :               : pnm->mkNode(
     101                 :            :                   ProofRule::MACRO_SR_PRED_TRANSFORM,
     102         [ -  - ]:          0 :                   {pfNotLit, teq.getGenerator()->getProofFor(teq.getProven())},
     103                 :       2534 :                   {rawEq.negate()});
     104                 :            :       Pf pfBot =
     105                 :            :           pnm->mkNode(ProofRule::CONTRA,
     106                 :            :                       {pnm->mkNode(ProofRule::ARITH_TRICHOTOMY,
     107                 :       2534 :                                    {pnm->mkAssume(less.negate()), pfNotRawEq},
     108                 :            :                                    {},
     109                 :       2534 :                                    greater),
     110                 :       2534 :                        pnm->mkAssume(greater.negate())},
     111                 :      13937 :                       {});
     112                 :            :       std::vector<Node> assumptions = {
     113                 :       7602 :           literal.negate(), less.negate(), greater.negate()};
     114                 :            :       // Proof of (not (and (not (= v i)) (not (< v i)) (not (> v i))))
     115                 :       3801 :       Pf pfNotAnd = pnm->mkScope(pfBot, assumptions);
     116                 :            :       Pf pfL = pnm->mkNode(ProofRule::MACRO_SR_PRED_TRANSFORM,
     117                 :       1267 :                            {pnm->mkNode(ProofRule::NOT_AND, {pfNotAnd}, {})},
     118                 :       8869 :                            {l});
     119                 :       1267 :       lems.push_back(d_pfGen->mkTrustNode(l, pfL));
     120                 :            :     }
     121                 :            :     else
     122                 :            :     {
     123                 :       1949 :       lems.push_back(TrustNode::mkTrustLemma(l, nullptr));
     124                 :            :     }
     125                 :            :   }
     126                 :            :   else
     127                 :            :   {
     128                 :         12 :     Node ub = rewrite(nm->mkNode(Kind::LEQ, var, nm->mkConstInt(floor)));
     129                 :            :     // Similar to above, the rewritten form should be a GEQ literal, otherwise
     130                 :            :     // the split returned by this method will not have its intended effect
     131                 :          8 :     Assert(ub.getKind() == Kind::GEQ
     132                 :            :            || (ub.getKind() == Kind::NOT && ub[0].getKind() == Kind::GEQ));
     133                 :          8 :     Node lb = ub.notNode();
     134         [ -  + ]:          4 :     if (proofsEnabled())
     135                 :            :     {
     136                 :          0 :       lems.push_back(d_pfGen->mkTrustNode(
     137                 :          0 :           nm->mkNode(Kind::OR, ub, lb), ProofRule::SPLIT, {}, {ub}));
     138                 :            :     }
     139                 :            :     else
     140                 :            :     {
     141                 :          4 :       lems.push_back(
     142                 :          8 :           TrustNode::mkTrustLemma(nm->mkNode(Kind::OR, ub, lb), nullptr));
     143                 :            :     }
     144                 :            :   }
     145         [ -  + ]:       3220 :   if (TraceIsOn("integers"))
     146                 :            :   {
     147         [ -  - ]:          0 :     Trace("integers") << "integers: branch & bound:";
     148         [ -  - ]:          0 :     for (const TrustNode& tn : lems)
     149                 :            :     {
     150         [ -  - ]:          0 :       Trace("integers") << " " << tn;
     151                 :            :     }
     152         [ -  - ]:          0 :     Trace("integers") << std::endl;
     153                 :            :   }
     154                 :       6440 :   return lems;
     155                 :            : }
     156                 :            : 
     157                 :       3220 : bool BranchAndBound::proofsEnabled() const
     158                 :            : {
     159                 :       3220 :   return d_env.isTheoryProofProducing();
     160                 :            : }
     161                 :            : 
     162                 :            : }  // namespace arith
     163                 :            : }  // namespace theory
     164                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14