1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Alex Ozdemir, Gereon Kremer, Andres Noetzli |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
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 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Implementation of arithmetic proof checker. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/arith/proof_checker.h" |
17 |
|
|
18 |
|
#include <iostream> |
19 |
|
#include <set> |
20 |
|
|
21 |
|
#include "expr/skolem_manager.h" |
22 |
|
#include "theory/arith/arith_utilities.h" |
23 |
|
#include "theory/arith/constraint.h" |
24 |
|
#include "theory/arith/normal_form.h" |
25 |
|
#include "theory/arith/operator_elim.h" |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace theory { |
29 |
|
namespace arith { |
30 |
|
|
31 |
3769 |
void ArithProofRuleChecker::registerTo(ProofChecker* pc) |
32 |
|
{ |
33 |
3769 |
pc->registerChecker(PfRule::MACRO_ARITH_SCALE_SUM_UB, this); |
34 |
3769 |
pc->registerChecker(PfRule::ARITH_SUM_UB, this); |
35 |
3769 |
pc->registerChecker(PfRule::ARITH_TRICHOTOMY, this); |
36 |
3769 |
pc->registerChecker(PfRule::INT_TIGHT_UB, this); |
37 |
3769 |
pc->registerChecker(PfRule::INT_TIGHT_LB, this); |
38 |
3769 |
pc->registerChecker(PfRule::ARITH_OP_ELIM_AXIOM, this); |
39 |
|
|
40 |
3769 |
pc->registerChecker(PfRule::ARITH_MULT_POS, this); |
41 |
3769 |
pc->registerChecker(PfRule::ARITH_MULT_NEG, this); |
42 |
|
// trusted rules |
43 |
3769 |
pc->registerTrustedChecker(PfRule::INT_TRUST, this, 2); |
44 |
3769 |
} |
45 |
|
|
46 |
252348 |
Node ArithProofRuleChecker::checkInternal(PfRule id, |
47 |
|
const std::vector<Node>& children, |
48 |
|
const std::vector<Node>& args) |
49 |
|
{ |
50 |
252348 |
NodeManager* nm = NodeManager::currentNM(); |
51 |
504696 |
auto zero = nm->mkConst<Rational>(0); |
52 |
252348 |
if (Debug.isOn("arith::pf::check")) |
53 |
|
{ |
54 |
|
Debug("arith::pf::check") << "Arith PfRule:" << id << std::endl; |
55 |
|
Debug("arith::pf::check") << " children: " << std::endl; |
56 |
|
for (const auto& c : children) |
57 |
|
{ |
58 |
|
Debug("arith::pf::check") << " * " << c << std::endl; |
59 |
|
} |
60 |
|
Debug("arith::pf::check") << " args:" << std::endl; |
61 |
|
for (const auto& c : args) |
62 |
|
{ |
63 |
|
Debug("arith::pf::check") << " * " << c << std::endl; |
64 |
|
} |
65 |
|
} |
66 |
252348 |
switch (id) |
67 |
|
{ |
68 |
24466 |
case PfRule::ARITH_MULT_POS: |
69 |
|
{ |
70 |
24466 |
Assert(children.empty()); |
71 |
24466 |
Assert(args.size() == 2); |
72 |
48932 |
Node mult = args[0]; |
73 |
24466 |
Kind rel = args[1].getKind(); |
74 |
24466 |
Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT |
75 |
|
|| rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ); |
76 |
48932 |
Node lhs = args[1][0]; |
77 |
48932 |
Node rhs = args[1][1]; |
78 |
|
return nm->mkNode(Kind::IMPLIES, |
79 |
122330 |
nm->mkAnd(std::vector<Node>{ |
80 |
73398 |
nm->mkNode(Kind::GT, mult, zero), args[1]}), |
81 |
97864 |
nm->mkNode(rel, |
82 |
48932 |
nm->mkNode(Kind::MULT, mult, lhs), |
83 |
122330 |
nm->mkNode(Kind::MULT, mult, rhs))); |
84 |
|
} |
85 |
27539 |
case PfRule::ARITH_MULT_NEG: |
86 |
|
{ |
87 |
27539 |
Assert(children.empty()); |
88 |
27539 |
Assert(args.size() == 2); |
89 |
55078 |
Node mult = args[0]; |
90 |
27539 |
Kind rel = args[1].getKind(); |
91 |
27539 |
Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT |
92 |
|
|| rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ); |
93 |
27539 |
Kind rel_inv = (rel == Kind::DISTINCT ? rel : reverseRelationKind(rel)); |
94 |
55078 |
Node lhs = args[1][0]; |
95 |
55078 |
Node rhs = args[1][1]; |
96 |
|
return nm->mkNode(Kind::IMPLIES, |
97 |
137695 |
nm->mkAnd(std::vector<Node>{ |
98 |
82617 |
nm->mkNode(Kind::LT, mult, zero), args[1]}), |
99 |
110156 |
nm->mkNode(rel_inv, |
100 |
55078 |
nm->mkNode(Kind::MULT, mult, lhs), |
101 |
137695 |
nm->mkNode(Kind::MULT, mult, rhs))); |
102 |
|
} |
103 |
9582 |
case PfRule::ARITH_SUM_UB: |
104 |
|
{ |
105 |
9582 |
if (children.size() < 2) |
106 |
|
{ |
107 |
|
return Node::null(); |
108 |
|
} |
109 |
|
|
110 |
|
// Whether a strict inequality is in the sum. |
111 |
9582 |
bool strict = false; |
112 |
19164 |
NodeBuilder leftSum(Kind::PLUS); |
113 |
19164 |
NodeBuilder rightSum(Kind::PLUS); |
114 |
61112 |
for (size_t i = 0; i < children.size(); ++i) |
115 |
|
{ |
116 |
|
// Adjust strictness |
117 |
51530 |
switch (children[i].getKind()) |
118 |
|
{ |
119 |
8934 |
case Kind::LT: |
120 |
|
{ |
121 |
8934 |
strict = true; |
122 |
8934 |
break; |
123 |
|
} |
124 |
42596 |
case Kind::LEQ: |
125 |
|
case Kind::EQUAL: |
126 |
|
{ |
127 |
42596 |
break; |
128 |
|
} |
129 |
|
default: |
130 |
|
{ |
131 |
|
Debug("arith::pf::check") |
132 |
|
<< "Bad kind: " << children[i].getKind() << std::endl; |
133 |
|
return Node::null(); |
134 |
|
} |
135 |
|
} |
136 |
51530 |
leftSum << children[i][0]; |
137 |
51530 |
rightSum << children[i][1]; |
138 |
|
} |
139 |
|
Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ, |
140 |
19164 |
leftSum.constructNode(), |
141 |
38328 |
rightSum.constructNode()); |
142 |
9582 |
return r; |
143 |
|
} |
144 |
77614 |
case PfRule::MACRO_ARITH_SCALE_SUM_UB: |
145 |
|
{ |
146 |
|
//================================================= Arithmetic rules |
147 |
|
// ======== Adding Inequalities |
148 |
|
// Note: an ArithLiteral is a term of the form (>< poly const) |
149 |
|
// where |
150 |
|
// >< is >=, >, ==, <, <=, or not(== ...). |
151 |
|
// poly is a polynomial |
152 |
|
// const is a rational constant |
153 |
|
|
154 |
|
// Children: (P1:l1, ..., Pn:ln) |
155 |
|
// where each li is an ArithLiteral |
156 |
|
// not(= ...) is dis-allowed! |
157 |
|
// |
158 |
|
// Arguments: (k1, ..., kn), non-zero reals |
159 |
|
// --------------------- |
160 |
|
// Conclusion: (>< t1 t2) |
161 |
|
// where >< is the fusion of the combination of the ><i, (flipping each |
162 |
|
// it its ki is negative). >< is always one of <, <= NB: this implies |
163 |
|
// that lower bounds must have negative ki, |
164 |
|
// and upper bounds must have positive ki. |
165 |
|
// t1 is the sum of the scaled polynomials (k_1 * poly_1 + ... + k_n * |
166 |
|
// poly_n) t2 is the sum of the scaled constants (k_1 * const_1 + ... + |
167 |
|
// k_n * const_n) |
168 |
77614 |
Assert(children.size() == args.size()); |
169 |
77614 |
if (children.size() < 2) |
170 |
|
{ |
171 |
|
return Node::null(); |
172 |
|
} |
173 |
|
|
174 |
|
// Whether a strict inequality is in the sum. |
175 |
77614 |
bool strict = false; |
176 |
155228 |
NodeBuilder leftSum(Kind::PLUS); |
177 |
155228 |
NodeBuilder rightSum(Kind::PLUS); |
178 |
332193 |
for (size_t i = 0; i < children.size(); ++i) |
179 |
|
{ |
180 |
509158 |
Rational scalar = args[i].getConst<Rational>(); |
181 |
254579 |
if (scalar == 0) |
182 |
|
{ |
183 |
|
Debug("arith::pf::check") << "Error: zero scalar" << std::endl; |
184 |
|
return Node::null(); |
185 |
|
} |
186 |
|
|
187 |
|
// Adjust strictness |
188 |
254579 |
switch (children[i].getKind()) |
189 |
|
{ |
190 |
48866 |
case Kind::GT: |
191 |
|
case Kind::LT: |
192 |
|
{ |
193 |
48866 |
strict = true; |
194 |
48866 |
break; |
195 |
|
} |
196 |
205713 |
case Kind::GEQ: |
197 |
|
case Kind::LEQ: |
198 |
|
case Kind::EQUAL: |
199 |
|
{ |
200 |
205713 |
break; |
201 |
|
} |
202 |
|
default: |
203 |
|
{ |
204 |
|
Debug("arith::pf::check") |
205 |
|
<< "Bad kind: " << children[i].getKind() << std::endl; |
206 |
|
} |
207 |
|
} |
208 |
|
// Check sign |
209 |
254579 |
switch (children[i].getKind()) |
210 |
|
{ |
211 |
71886 |
case Kind::GT: |
212 |
|
case Kind::GEQ: |
213 |
|
{ |
214 |
71886 |
if (scalar > 0) |
215 |
|
{ |
216 |
|
Debug("arith::pf::check") |
217 |
|
<< "Positive scalar for lower bound: " << scalar << " for " |
218 |
|
<< children[i] << std::endl; |
219 |
|
return Node::null(); |
220 |
|
} |
221 |
71886 |
break; |
222 |
|
} |
223 |
77753 |
case Kind::LEQ: |
224 |
|
case Kind::LT: |
225 |
|
{ |
226 |
77753 |
if (scalar < 0) |
227 |
|
{ |
228 |
|
Debug("arith::pf::check") |
229 |
|
<< "Negative scalar for upper bound: " << scalar << " for " |
230 |
|
<< children[i] << std::endl; |
231 |
|
return Node::null(); |
232 |
|
} |
233 |
77753 |
break; |
234 |
|
} |
235 |
104940 |
case Kind::EQUAL: |
236 |
|
{ |
237 |
104940 |
break; |
238 |
|
} |
239 |
|
default: |
240 |
|
{ |
241 |
|
Debug("arith::pf::check") |
242 |
|
<< "Bad kind: " << children[i].getKind() << std::endl; |
243 |
|
} |
244 |
|
} |
245 |
1272895 |
leftSum << nm->mkNode( |
246 |
1272895 |
Kind::MULT, nm->mkConst<Rational>(scalar), children[i][0]); |
247 |
1272895 |
rightSum << nm->mkNode( |
248 |
1272895 |
Kind::MULT, nm->mkConst<Rational>(scalar), children[i][1]); |
249 |
|
} |
250 |
|
Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ, |
251 |
155228 |
leftSum.constructNode(), |
252 |
310456 |
rightSum.constructNode()); |
253 |
77614 |
return r; |
254 |
|
} |
255 |
1455 |
case PfRule::INT_TIGHT_LB: |
256 |
|
{ |
257 |
|
// Children: (P:(> i c)) |
258 |
|
// where i has integer type. |
259 |
|
// Arguments: none |
260 |
|
// --------------------- |
261 |
|
// Conclusion: (>= i leastIntGreaterThan(c)}) |
262 |
2910 |
if (children.size() != 1 |
263 |
1455 |
|| (children[0].getKind() != Kind::GT |
264 |
|
&& children[0].getKind() != Kind::GEQ) |
265 |
2910 |
|| !children[0][0].getType().isInteger() |
266 |
4365 |
|| children[0][1].getKind() != Kind::CONST_RATIONAL) |
267 |
|
{ |
268 |
|
Debug("arith::pf::check") << "Illformed input: " << children; |
269 |
|
return Node::null(); |
270 |
|
} |
271 |
|
else |
272 |
|
{ |
273 |
2910 |
Rational originalBound = children[0][1].getConst<Rational>(); |
274 |
2910 |
Rational newBound = leastIntGreaterThan(originalBound); |
275 |
2910 |
Node rational = nm->mkConst<Rational>(newBound); |
276 |
1455 |
return nm->mkNode(kind::GEQ, children[0][0], rational); |
277 |
|
} |
278 |
|
} |
279 |
63052 |
case PfRule::INT_TIGHT_UB: |
280 |
|
{ |
281 |
|
// ======== Tightening Strict Integer Upper Bounds |
282 |
|
// Children: (P:(< i c)) |
283 |
|
// where i has integer type. |
284 |
|
// Arguments: none |
285 |
|
// --------------------- |
286 |
|
// Conclusion: (<= i greatestIntLessThan(c)}) |
287 |
126104 |
if (children.size() != 1 |
288 |
63052 |
|| (children[0].getKind() != Kind::LT |
289 |
|
&& children[0].getKind() != Kind::LEQ) |
290 |
126104 |
|| !children[0][0].getType().isInteger() |
291 |
189156 |
|| children[0][1].getKind() != Kind::CONST_RATIONAL) |
292 |
|
{ |
293 |
|
Debug("arith::pf::check") << "Illformed input: " << children; |
294 |
|
return Node::null(); |
295 |
|
} |
296 |
|
else |
297 |
|
{ |
298 |
126104 |
Rational originalBound = children[0][1].getConst<Rational>(); |
299 |
126104 |
Rational newBound = greatestIntLessThan(originalBound); |
300 |
126104 |
Node rational = nm->mkConst<Rational>(newBound); |
301 |
63052 |
return nm->mkNode(kind::LEQ, children[0][0], rational); |
302 |
|
} |
303 |
|
} |
304 |
45236 |
case PfRule::ARITH_TRICHOTOMY: |
305 |
|
{ |
306 |
90472 |
Node a = negateProofLiteral(children[0]); |
307 |
90472 |
Node b = negateProofLiteral(children[1]); |
308 |
90472 |
Node c = args[0]; |
309 |
45236 |
if (a[0] == b[0] && b[0] == c[0] && a[1] == b[1] && b[1] == c[1]) |
310 |
|
{ |
311 |
90472 |
std::set<Kind> cmps; |
312 |
45236 |
cmps.insert(a.getKind()); |
313 |
45236 |
cmps.insert(b.getKind()); |
314 |
45236 |
cmps.insert(c.getKind()); |
315 |
45236 |
if (cmps.count(Kind::EQUAL) == 0) |
316 |
|
{ |
317 |
|
Debug("arith::pf::check") << "Error: No = " << std::endl; |
318 |
|
return Node::null(); |
319 |
|
} |
320 |
45236 |
if (cmps.count(Kind::GT) == 0) |
321 |
|
{ |
322 |
|
Debug("arith::pf::check") << "Error: No > " << std::endl; |
323 |
|
return Node::null(); |
324 |
|
} |
325 |
45236 |
if (cmps.count(Kind::LT) == 0) |
326 |
|
{ |
327 |
|
Debug("arith::pf::check") << "Error: No < " << std::endl; |
328 |
|
return Node::null(); |
329 |
|
} |
330 |
45236 |
return args[0]; |
331 |
|
} |
332 |
|
else |
333 |
|
{ |
334 |
|
Debug("arith::pf::check") |
335 |
|
<< "Error: Different polynomials / values" << std::endl; |
336 |
|
Debug("arith::pf::check") << " a: " << a << std::endl; |
337 |
|
Debug("arith::pf::check") << " b: " << b << std::endl; |
338 |
|
Debug("arith::pf::check") << " c: " << c << std::endl; |
339 |
|
return Node::null(); |
340 |
|
} |
341 |
|
// Check that all have the same constant: |
342 |
|
} |
343 |
3404 |
case PfRule::INT_TRUST: |
344 |
|
{ |
345 |
3404 |
if (Debug.isOn("arith::pf::check::trust")) |
346 |
|
{ |
347 |
|
Debug("arith::pf::check::trust") << "Arith PfRule:" << id << std::endl; |
348 |
|
Debug("arith::pf::check::trust") << " children: " << std::endl; |
349 |
|
for (const auto& c : children) |
350 |
|
{ |
351 |
|
Debug("arith::pf::check::trust") << " * " << c << std::endl; |
352 |
|
} |
353 |
|
Debug("arith::pf::check::trust") << " args:" << std::endl; |
354 |
|
for (const auto& c : args) |
355 |
|
{ |
356 |
|
Debug("arith::pf::check::trust") << " * " << c << std::endl; |
357 |
|
} |
358 |
|
} |
359 |
3404 |
Assert(args.size() == 1); |
360 |
3404 |
return args[0]; |
361 |
|
} |
362 |
|
case PfRule::ARITH_OP_ELIM_AXIOM: |
363 |
|
{ |
364 |
|
Assert(children.empty()); |
365 |
|
Assert(args.size() == 1); |
366 |
|
return OperatorElim::getAxiomFor(args[0]); |
367 |
|
} |
368 |
|
default: return Node::null(); |
369 |
|
} |
370 |
|
} |
371 |
|
} // namespace arith |
372 |
|
} // namespace theory |
373 |
29349 |
} // namespace cvc5 |