1 /******************************************************************************
2 * Top contributors (to current version):
5 * This file is part of the cvc5 project.
7 * Copyright (c) 2009-2021 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 * ****************************************************************************
13 * Branch and bound for arithmetic
16 #include "theory/arith/branch_and_bound.h"
18 #include "options/arith_options.h"
19 #include "proof/eager_proof_generator.h"
20 #include "proof/proof_node.h"
21 #include "theory/arith/arith_utilities.h"
22 #include "theory/rewriter.h"
23 #include "theory/theory.h"
25 using namespace cvc5::kind
;
31 BranchAndBound::BranchAndBound(ArithState
& s
,
33 PreprocessRewriteEq
& ppre
,
34 ProofNodeManager
* pnm
)
38 d_pfGen(new EagerProofGenerator(pnm
, s
.getUserContext())),
43 TrustNode
BranchAndBound::branchIntegerVariable(TNode var
, Rational value
)
45 TrustNode lem
= TrustNode::null();
46 NodeManager
* nm
= NodeManager::currentNM();
47 Integer floor
= value
.floor();
48 if (d_astate
.options().arith
.brabTest
)
50 Trace("integers") << "branch-round-and-bound enabled" << std::endl
;
51 Integer ceil
= value
.ceiling();
52 Rational f
= value
- floor
;
53 // Multiply by -1 to get abs value.
54 Rational c
= (value
- ceil
) * (-1);
55 Integer nearest
= (c
> f
) ? floor
: ceil
;
57 // Prioritize trying a simple rounding of the real solution first,
58 // it that fails, fall back on original branch and bound strategy.
60 Rewriter::rewrite(nm
->mkNode(LEQ
, var
, mkRationalNode(nearest
- 1)));
62 Rewriter::rewrite(nm
->mkNode(GEQ
, var
, mkRationalNode(nearest
+ 1)));
63 Node right
= nm
->mkNode(OR
, ub
, lb
);
64 Node rawEq
= nm
->mkNode(EQUAL
, var
, mkRationalNode(nearest
));
65 Node eq
= Rewriter::rewrite(rawEq
);
66 // Also preprocess it before we send it out. This is important since
67 // arithmetic may prefer eliminating equalities.
69 if (Theory::theoryOf(eq
) == THEORY_ARITH
)
71 teq
= d_ppre
.ppRewriteEq(eq
);
72 eq
= teq
.isNull() ? eq
: teq
.getNode();
74 Node literal
= d_astate
.getValuation().ensureLiteral(eq
);
75 Trace("integers") << "eq: " << eq
<< "\nto: " << literal
<< std::endl
;
76 d_im
.requirePhase(literal
, true);
77 Node l
= nm
->mkNode(OR
, literal
, right
);
78 Trace("integers") << "l: " << l
<< std::endl
;
81 Node less
= nm
->mkNode(LT
, var
, mkRationalNode(nearest
));
82 Node greater
= nm
->mkNode(GT
, var
, mkRationalNode(nearest
));
83 // TODO (project #37): justify. Thread proofs through *ensureLiteral*.
84 Debug("integers::pf") << "less: " << less
<< std::endl
;
85 Debug("integers::pf") << "greater: " << greater
<< std::endl
;
86 Debug("integers::pf") << "literal: " << literal
<< std::endl
;
87 Debug("integers::pf") << "eq: " << eq
<< std::endl
;
88 Debug("integers::pf") << "rawEq: " << rawEq
<< std::endl
;
89 Pf pfNotLit
= d_pnm
->mkAssume(literal
.negate());
90 // rewrite notLiteral to notRawEq, using teq.
95 PfRule::MACRO_SR_PRED_TRANSFORM
,
97 teq
.getGenerator()->getProofFor(teq
.getProven())},
99 Pf pfBot
= d_pnm
->mkNode(
101 {d_pnm
->mkNode(PfRule::ARITH_TRICHOTOMY
,
102 {d_pnm
->mkAssume(less
.negate()), pfNotRawEq
},
104 d_pnm
->mkAssume(greater
.negate())},
106 std::vector
<Node
> assumptions
= {
107 literal
.negate(), less
.negate(), greater
.negate()};
108 // Proof of (not (and (not (= v i)) (not (< v i)) (not (> v i))))
109 Pf pfNotAnd
= d_pnm
->mkScope(pfBot
, assumptions
);
110 Pf pfL
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
,
111 {d_pnm
->mkNode(PfRule::NOT_AND
, {pfNotAnd
}, {})},
113 lem
= d_pfGen
->mkTrustNode(l
, pfL
);
117 lem
= TrustNode::mkTrustLemma(l
, nullptr);
122 Node ub
= Rewriter::rewrite(nm
->mkNode(LEQ
, var
, mkRationalNode(floor
)));
123 Node lb
= ub
.notNode();
127 d_pfGen
->mkTrustNode(nm
->mkNode(OR
, ub
, lb
), PfRule::SPLIT
, {}, {ub
});
131 lem
= TrustNode::mkTrustLemma(nm
->mkNode(OR
, ub
, lb
), nullptr);
135 Trace("integers") << "integers: branch & bound: " << lem
<< std::endl
;
139 bool BranchAndBound::proofsEnabled() const { return d_pnm
!= nullptr; }
142 } // namespace theory