1 /******************************************************************************
2 * Top contributors (to current version):
3 * Tim King, Dejan Jovanovic, Andres Noetzli
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 * [[ Add one-line brief description here ]]
15 * [[ Add lengthier description here ]]
16 * \todo document this file
21 #include "base/output.h"
22 #include "expr/node_algorithm.h"
23 #include "options/arith_options.h"
24 #include "smt/smt_statistics_registry.h"
25 #include "theory/arith/arith_static_learner.h"
26 #include "theory/arith/arith_utilities.h"
27 #include "theory/arith/normal_form.h"
28 #include "theory/rewriter.h"
31 using namespace cvc5::kind
;
38 ArithStaticLearner::ArithStaticLearner(context::Context
* userContext
) :
39 d_minMap(userContext
),
40 d_maxMap(userContext
),
45 ArithStaticLearner::~ArithStaticLearner(){
48 ArithStaticLearner::Statistics::Statistics()
49 : d_iteMinMaxApplications(smtStatisticsRegistry().registerInt(
50 "theory::arith::iteMinMaxApplications")),
51 d_iteConstantApplications(smtStatisticsRegistry().registerInt(
52 "theory::arith::iteConstantApplications"))
56 void ArithStaticLearner::staticLearning(TNode n
, NodeBuilder
& learned
)
58 vector
<TNode
> workList
;
59 workList
.push_back(n
);
62 //Contains an underapproximation of nodes that must hold.
67 while(!workList
.empty()) {
70 bool unprocessedChildren
= false;
71 for(TNode::iterator i
= n
.begin(), iend
= n
.end(); i
!= iend
; ++i
) {
72 if(processed
.find(*i
) == processed
.end()) {
74 workList
.push_back(*i
);
75 unprocessedChildren
= true;
78 if(n
.getKind() == AND
&& defTrue
.find(n
) != defTrue
.end() ){
79 for(TNode::iterator i
= n
.begin(), iend
= n
.end(); i
!= iend
; ++i
) {
84 if(unprocessedChildren
) {
89 // has node n been processed in the meantime ?
90 if(processed
.find(n
) != processed
.end()) {
95 process(n
,learned
, defTrue
);
100 void ArithStaticLearner::process(TNode n
,
101 NodeBuilder
& learned
,
102 const TNodeSet
& defTrue
)
104 Debug("arith::static") << "===================== looking at " << n
<< endl
;
108 if (expr::hasBoundVar(n
))
110 // Unsafe with non-ground ITEs; do nothing
111 Debug("arith::static")
112 << "(potentially) non-ground ITE, ignoring..." << endl
;
116 if(n
[0].getKind() != EQUAL
&&
117 isRelationOperator(n
[0].getKind()) ){
118 iteMinMax(n
, learned
);
121 if((d_minMap
.find(n
[1]) != d_minMap
.end() && d_minMap
.find(n
[2]) != d_minMap
.end()) ||
122 (d_maxMap
.find(n
[1]) != d_maxMap
.end() && d_maxMap
.find(n
[2]) != d_maxMap
.end())) {
123 iteConstant(n
, learned
);
128 // Mark constants as minmax
129 d_minMap
.insert(n
, n
.getConst
<Rational
>());
130 d_maxMap
.insert(n
, n
.getConst
<Rational
>());
132 default: // Do nothing
137 void ArithStaticLearner::iteMinMax(TNode n
, NodeBuilder
& learned
)
139 Assert(n
.getKind() == kind::ITE
);
140 Assert(n
[0].getKind() != EQUAL
);
141 Assert(isRelationOperator(n
[0].getKind()));
144 Kind k
= oldSimplifiedKind(c
);
147 TNode cleft
= (c
.getKind() == NOT
) ? c
[0][0] : c
[0];
148 TNode cright
= (c
.getKind() == NOT
) ? c
[0][1] : c
[1];
150 if((t
== cright
) && (e
== cleft
)){
154 k
= reverseRelationKind(k
);
158 //(ite (x - y < 0) x y)
160 // (ite (x - y < -c) )
162 if(t
== cleft
&& e
== cright
){
163 // t == cleft && e == cright
167 case LT
: // (ite (< x y) x y)
168 case LEQ
: { // (ite (<= x y) x y)
169 Node nLeqX
= NodeBuilder(LEQ
) << n
<< t
;
170 Node nLeqY
= NodeBuilder(LEQ
) << n
<< e
;
171 Debug("arith::static") << n
<< "is a min =>" << nLeqX
<< nLeqY
<< endl
;
172 learned
<< nLeqX
<< nLeqY
;
173 ++(d_statistics
.d_iteMinMaxApplications
);
176 case GT
: // (ite (> x y) x y)
177 case GEQ
: { // (ite (>= x y) x y)
178 Node nGeqX
= NodeBuilder(GEQ
) << n
<< t
;
179 Node nGeqY
= NodeBuilder(GEQ
) << n
<< e
;
180 Debug("arith::static") << n
<< "is a max =>" << nGeqX
<< nGeqY
<< endl
;
181 learned
<< nGeqX
<< nGeqY
;
182 ++(d_statistics
.d_iteMinMaxApplications
);
185 default: Unreachable();
190 void ArithStaticLearner::iteConstant(TNode n
, NodeBuilder
& learned
)
192 Assert(n
.getKind() == ITE
);
194 Debug("arith::static") << "iteConstant(" << n
<< ")" << endl
;
196 if (d_minMap
.find(n
[1]) != d_minMap
.end() && d_minMap
.find(n
[2]) != d_minMap
.end()) {
197 const DeltaRational
& first
= d_minMap
[n
[1]];
198 const DeltaRational
& second
= d_minMap
[n
[2]];
199 DeltaRational min
= std::min(first
, second
);
200 CDNodeToMinMaxMap::const_iterator minFind
= d_minMap
.find(n
);
201 if (minFind
== d_minMap
.end() || (*minFind
).second
< min
) {
202 d_minMap
.insert(n
, min
);
203 NodeManager
* nm
= NodeManager::currentNM();
204 Node nGeqMin
= nm
->mkNode(
205 min
.getInfinitesimalPart() == 0 ? kind::GEQ
: kind::GT
,
207 nm
->mkConstRealOrInt(n
.getType(), min
.getNoninfinitesimalPart()));
209 Debug("arith::static") << n
<< " iteConstant" << nGeqMin
<< endl
;
210 ++(d_statistics
.d_iteConstantApplications
);
214 if (d_maxMap
.find(n
[1]) != d_maxMap
.end() && d_maxMap
.find(n
[2]) != d_maxMap
.end()) {
215 const DeltaRational
& first
= d_maxMap
[n
[1]];
216 const DeltaRational
& second
= d_maxMap
[n
[2]];
217 DeltaRational max
= std::max(first
, second
);
218 CDNodeToMinMaxMap::const_iterator maxFind
= d_maxMap
.find(n
);
219 if (maxFind
== d_maxMap
.end() || (*maxFind
).second
> max
) {
220 d_maxMap
.insert(n
, max
);
221 NodeManager
* nm
= NodeManager::currentNM();
222 Node nLeqMax
= nm
->mkNode(
223 max
.getInfinitesimalPart() == 0 ? kind::LEQ
: kind::LT
,
225 nm
->mkConstRealOrInt(n
.getType(), max
.getNoninfinitesimalPart()));
227 Debug("arith::static") << n
<< " iteConstant" << nLeqMax
<< endl
;
228 ++(d_statistics
.d_iteConstantApplications
);
233 std::set
<Node
> listToSet(TNode l
){
235 while(l
.getKind() == OR
){
236 Assert(l
.getNumChildren() == 2);
243 void ArithStaticLearner::addBound(TNode n
) {
245 CDNodeToMinMaxMap::const_iterator minFind
= d_minMap
.find(n
[0]);
246 CDNodeToMinMaxMap::const_iterator maxFind
= d_maxMap
.find(n
[0]);
248 Rational constant
= n
[1].getConst
<Rational
>();
249 DeltaRational bound
= constant
;
251 switch(Kind k
= n
.getKind()) {
252 case kind::LT
: bound
= DeltaRational(constant
, -1); CVC5_FALLTHROUGH
;
254 if (maxFind
== d_maxMap
.end() || (*maxFind
).second
> bound
)
256 d_maxMap
.insert(n
[0], bound
);
257 Debug("arith::static") << "adding bound " << n
<< endl
;
260 case kind::GT
: bound
= DeltaRational(constant
, 1); CVC5_FALLTHROUGH
;
262 if (minFind
== d_minMap
.end() || (*minFind
).second
< bound
)
264 d_minMap
.insert(n
[0], bound
);
265 Debug("arith::static") << "adding bound " << n
<< endl
;
268 default: Unhandled() << k
; break;
273 } // namespace theory