GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/proof_checker.cpp Lines: 122 174 70.1 %
Date: 2021-09-16 Branches: 253 857 29.5 %

Line Exec Source
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
3796
void ArithProofRuleChecker::registerTo(ProofChecker* pc)
32
{
33
3796
  pc->registerChecker(PfRule::MACRO_ARITH_SCALE_SUM_UB, this);
34
3796
  pc->registerChecker(PfRule::ARITH_SUM_UB, this);
35
3796
  pc->registerChecker(PfRule::ARITH_TRICHOTOMY, this);
36
3796
  pc->registerChecker(PfRule::INT_TIGHT_UB, this);
37
3796
  pc->registerChecker(PfRule::INT_TIGHT_LB, this);
38
3796
  pc->registerChecker(PfRule::ARITH_OP_ELIM_AXIOM, this);
39
3796
  pc->registerChecker(PfRule::ARITH_MULT_POS, this);
40
3796
  pc->registerChecker(PfRule::ARITH_MULT_NEG, this);
41
3796
}
42
43
192754
Node ArithProofRuleChecker::checkInternal(PfRule id,
44
                                          const std::vector<Node>& children,
45
                                          const std::vector<Node>& args)
46
{
47
192754
  NodeManager* nm = NodeManager::currentNM();
48
385508
  auto zero = nm->mkConst<Rational>(0);
49
192754
  if (Debug.isOn("arith::pf::check"))
50
  {
51
    Debug("arith::pf::check") << "Arith PfRule:" << id << std::endl;
52
    Debug("arith::pf::check") << "  children: " << std::endl;
53
    for (const auto& c : children)
54
    {
55
      Debug("arith::pf::check") << "  * " << c << std::endl;
56
    }
57
    Debug("arith::pf::check") << "  args:" << std::endl;
58
    for (const auto& c : args)
59
    {
60
      Debug("arith::pf::check") << "  * " << c << std::endl;
61
    }
62
  }
63
192754
  switch (id)
64
  {
65
23994
    case PfRule::ARITH_MULT_POS:
66
    {
67
23994
      Assert(children.empty());
68
23994
      Assert(args.size() == 2);
69
47988
      Node mult = args[0];
70
23994
      Kind rel = args[1].getKind();
71
23994
      Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT
72
             || rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ);
73
47988
      Node lhs = args[1][0];
74
47988
      Node rhs = args[1][1];
75
      return nm->mkNode(Kind::IMPLIES,
76
119970
                        nm->mkAnd(std::vector<Node>{
77
71982
                            nm->mkNode(Kind::GT, mult, zero), args[1]}),
78
95976
                        nm->mkNode(rel,
79
47988
                                   nm->mkNode(Kind::MULT, mult, lhs),
80
119970
                                   nm->mkNode(Kind::MULT, mult, rhs)));
81
    }
82
27147
    case PfRule::ARITH_MULT_NEG:
83
    {
84
27147
      Assert(children.empty());
85
27147
      Assert(args.size() == 2);
86
54294
      Node mult = args[0];
87
27147
      Kind rel = args[1].getKind();
88
27147
      Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT
89
             || rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ);
90
27147
      Kind rel_inv = (rel == Kind::DISTINCT ? rel : reverseRelationKind(rel));
91
54294
      Node lhs = args[1][0];
92
54294
      Node rhs = args[1][1];
93
      return nm->mkNode(Kind::IMPLIES,
94
135735
                        nm->mkAnd(std::vector<Node>{
95
81441
                            nm->mkNode(Kind::LT, mult, zero), args[1]}),
96
108588
                        nm->mkNode(rel_inv,
97
54294
                                   nm->mkNode(Kind::MULT, mult, lhs),
98
135735
                                   nm->mkNode(Kind::MULT, mult, rhs)));
99
    }
100
9722
    case PfRule::ARITH_SUM_UB:
101
    {
102
9722
      if (children.size() < 2)
103
      {
104
        return Node::null();
105
      }
106
107
      // Whether a strict inequality is in the sum.
108
9722
      bool strict = false;
109
19444
      NodeBuilder leftSum(Kind::PLUS);
110
19444
      NodeBuilder rightSum(Kind::PLUS);
111
62144
      for (size_t i = 0; i < children.size(); ++i)
112
      {
113
        // Adjust strictness
114
52422
        switch (children[i].getKind())
115
        {
116
9024
          case Kind::LT:
117
          {
118
9024
            strict = true;
119
9024
            break;
120
          }
121
43398
          case Kind::LEQ:
122
          case Kind::EQUAL:
123
          {
124
43398
            break;
125
          }
126
          default:
127
          {
128
            Debug("arith::pf::check")
129
                << "Bad kind: " << children[i].getKind() << std::endl;
130
            return Node::null();
131
          }
132
        }
133
52422
        leftSum << children[i][0];
134
52422
        rightSum << children[i][1];
135
      }
136
      Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ,
137
19444
                          leftSum.constructNode(),
138
38888
                          rightSum.constructNode());
139
9722
      return r;
140
    }
141
85144
    case PfRule::MACRO_ARITH_SCALE_SUM_UB:
142
    {
143
      //================================================= Arithmetic rules
144
      // ======== Adding Inequalities
145
      // Note: an ArithLiteral is a term of the form (>< poly const)
146
      // where
147
      //   >< is >=, >, ==, <, <=, or not(== ...).
148
      //   poly is a polynomial
149
      //   const is a rational constant
150
151
      // Children: (P1:l1, ..., Pn:ln)
152
      //           where each li is an ArithLiteral
153
      //           not(= ...) is dis-allowed!
154
      //
155
      // Arguments: (k1, ..., kn), non-zero reals
156
      // ---------------------
157
      // Conclusion: (>< t1 t2)
158
      //    where >< is the fusion of the combination of the ><i, (flipping each
159
      //    it its ki is negative). >< is always one of <, <= NB: this implies
160
      //    that lower bounds must have negative ki,
161
      //                      and upper bounds must have positive ki.
162
      //    t1 is the sum of the scaled polynomials (k_1 * poly_1 + ... + k_n *
163
      //    poly_n) t2 is the sum of the scaled constants (k_1 * const_1 + ... +
164
      //    k_n * const_n)
165
85144
      Assert(children.size() == args.size());
166
85144
      if (children.size() < 2)
167
      {
168
        return Node::null();
169
      }
170
171
      // Whether a strict inequality is in the sum.
172
85144
      bool strict = false;
173
170288
      NodeBuilder leftSum(Kind::PLUS);
174
170288
      NodeBuilder rightSum(Kind::PLUS);
175
365956
      for (size_t i = 0; i < children.size(); ++i)
176
      {
177
561624
        Rational scalar = args[i].getConst<Rational>();
178
280812
        if (scalar == 0)
179
        {
180
          Debug("arith::pf::check") << "Error: zero scalar" << std::endl;
181
          return Node::null();
182
        }
183
184
        // Adjust strictness
185
280812
        switch (children[i].getKind())
186
        {
187
53278
          case Kind::GT:
188
          case Kind::LT:
189
          {
190
53278
            strict = true;
191
53278
            break;
192
          }
193
227534
          case Kind::GEQ:
194
          case Kind::LEQ:
195
          case Kind::EQUAL:
196
          {
197
227534
            break;
198
          }
199
          default:
200
          {
201
            Debug("arith::pf::check")
202
                << "Bad kind: " << children[i].getKind() << std::endl;
203
          }
204
        }
205
        // Check sign
206
280812
        switch (children[i].getKind())
207
        {
208
76840
          case Kind::GT:
209
          case Kind::GEQ:
210
          {
211
76840
            if (scalar > 0)
212
            {
213
              Debug("arith::pf::check")
214
                  << "Positive scalar for lower bound: " << scalar << " for "
215
                  << children[i] << std::endl;
216
              return Node::null();
217
            }
218
76840
            break;
219
          }
220
84724
          case Kind::LEQ:
221
          case Kind::LT:
222
          {
223
84724
            if (scalar < 0)
224
            {
225
              Debug("arith::pf::check")
226
                  << "Negative scalar for upper bound: " << scalar << " for "
227
                  << children[i] << std::endl;
228
              return Node::null();
229
            }
230
84724
            break;
231
          }
232
119248
          case Kind::EQUAL:
233
          {
234
119248
            break;
235
          }
236
          default:
237
          {
238
            Debug("arith::pf::check")
239
                << "Bad kind: " << children[i].getKind() << std::endl;
240
          }
241
        }
242
1404060
        leftSum << nm->mkNode(
243
1404060
            Kind::MULT, nm->mkConst<Rational>(scalar), children[i][0]);
244
1404060
        rightSum << nm->mkNode(
245
1404060
            Kind::MULT, nm->mkConst<Rational>(scalar), children[i][1]);
246
      }
247
      Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ,
248
170288
                          leftSum.constructNode(),
249
340576
                          rightSum.constructNode());
250
85144
      return r;
251
    }
252
270
    case PfRule::INT_TIGHT_LB:
253
    {
254
      // Children: (P:(> i c))
255
      //         where i has integer type.
256
      // Arguments: none
257
      // ---------------------
258
      // Conclusion: (>= i leastIntGreaterThan(c)})
259
540
      if (children.size() != 1
260
270
          || (children[0].getKind() != Kind::GT
261
              && children[0].getKind() != Kind::GEQ)
262
540
          || !children[0][0].getType().isInteger()
263
810
          || children[0][1].getKind() != Kind::CONST_RATIONAL)
264
      {
265
        Debug("arith::pf::check") << "Illformed input: " << children;
266
        return Node::null();
267
      }
268
      else
269
      {
270
540
        Rational originalBound = children[0][1].getConst<Rational>();
271
540
        Rational newBound = leastIntGreaterThan(originalBound);
272
540
        Node rational = nm->mkConst<Rational>(newBound);
273
270
        return nm->mkNode(kind::GEQ, children[0][0], rational);
274
      }
275
    }
276
4135
    case PfRule::INT_TIGHT_UB:
277
    {
278
      // ======== Tightening Strict Integer Upper Bounds
279
      // Children: (P:(< i c))
280
      //         where i has integer type.
281
      // Arguments: none
282
      // ---------------------
283
      // Conclusion: (<= i greatestIntLessThan(c)})
284
8270
      if (children.size() != 1
285
4135
          || (children[0].getKind() != Kind::LT
286
              && children[0].getKind() != Kind::LEQ)
287
8270
          || !children[0][0].getType().isInteger()
288
12405
          || children[0][1].getKind() != Kind::CONST_RATIONAL)
289
      {
290
        Debug("arith::pf::check") << "Illformed input: " << children;
291
        return Node::null();
292
      }
293
      else
294
      {
295
8270
        Rational originalBound = children[0][1].getConst<Rational>();
296
8270
        Rational newBound = greatestIntLessThan(originalBound);
297
8270
        Node rational = nm->mkConst<Rational>(newBound);
298
4135
        return nm->mkNode(kind::LEQ, children[0][0], rational);
299
      }
300
    }
301
42342
    case PfRule::ARITH_TRICHOTOMY:
302
    {
303
84684
      Node a = negateProofLiteral(children[0]);
304
84684
      Node b = negateProofLiteral(children[1]);
305
84684
      Node c = args[0];
306
42342
      if (a[0] == b[0] && b[0] == c[0] && a[1] == b[1] && b[1] == c[1])
307
      {
308
84684
        std::set<Kind> cmps;
309
42342
        cmps.insert(a.getKind());
310
42342
        cmps.insert(b.getKind());
311
42342
        cmps.insert(c.getKind());
312
42342
        if (cmps.count(Kind::EQUAL) == 0)
313
        {
314
          Debug("arith::pf::check") << "Error: No = " << std::endl;
315
          return Node::null();
316
        }
317
42342
        if (cmps.count(Kind::GT) == 0)
318
        {
319
          Debug("arith::pf::check") << "Error: No > " << std::endl;
320
          return Node::null();
321
        }
322
42342
        if (cmps.count(Kind::LT) == 0)
323
        {
324
          Debug("arith::pf::check") << "Error: No < " << std::endl;
325
          return Node::null();
326
        }
327
42342
        return args[0];
328
      }
329
      else
330
      {
331
        Debug("arith::pf::check")
332
            << "Error: Different polynomials / values" << std::endl;
333
        Debug("arith::pf::check") << "  a: " << a << std::endl;
334
        Debug("arith::pf::check") << "  b: " << b << std::endl;
335
        Debug("arith::pf::check") << "  c: " << c << std::endl;
336
        return Node::null();
337
      }
338
      // Check that all have the same constant:
339
    }
340
    case PfRule::ARITH_OP_ELIM_AXIOM:
341
    {
342
      Assert(children.empty());
343
      Assert(args.size() == 1);
344
      return OperatorElim::getAxiomFor(args[0]);
345
    }
346
    default: return Node::null();
347
  }
348
}
349
}  // namespace arith
350
}  // namespace theory
351
29577
}  // namespace cvc5