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
|