GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/proof_checker.cpp Lines: 127 186 68.3 %
Date: 2021-08-06 Branches: 261 934 27.9 %

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
3768
void ArithProofRuleChecker::registerTo(ProofChecker* pc)
32
{
33
3768
  pc->registerChecker(PfRule::MACRO_ARITH_SCALE_SUM_UB, this);
34
3768
  pc->registerChecker(PfRule::ARITH_SUM_UB, this);
35
3768
  pc->registerChecker(PfRule::ARITH_TRICHOTOMY, this);
36
3768
  pc->registerChecker(PfRule::INT_TIGHT_UB, this);
37
3768
  pc->registerChecker(PfRule::INT_TIGHT_LB, this);
38
3768
  pc->registerChecker(PfRule::ARITH_OP_ELIM_AXIOM, this);
39
40
3768
  pc->registerChecker(PfRule::ARITH_MULT_POS, this);
41
3768
  pc->registerChecker(PfRule::ARITH_MULT_NEG, this);
42
  // trusted rules
43
3768
  pc->registerTrustedChecker(PfRule::INT_TRUST, this, 2);
44
3768
}
45
46
253870
Node ArithProofRuleChecker::checkInternal(PfRule id,
47
                                          const std::vector<Node>& children,
48
                                          const std::vector<Node>& args)
49
{
50
253870
  NodeManager* nm = NodeManager::currentNM();
51
507740
  auto zero = nm->mkConst<Rational>(0);
52
253870
  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
253870
  switch (id)
67
  {
68
24488
    case PfRule::ARITH_MULT_POS:
69
    {
70
24488
      Assert(children.empty());
71
24488
      Assert(args.size() == 2);
72
48976
      Node mult = args[0];
73
24488
      Kind rel = args[1].getKind();
74
24488
      Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT
75
             || rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ);
76
48976
      Node lhs = args[1][0];
77
48976
      Node rhs = args[1][1];
78
      return nm->mkNode(Kind::IMPLIES,
79
122440
                        nm->mkAnd(std::vector<Node>{
80
73464
                            nm->mkNode(Kind::GT, mult, zero), args[1]}),
81
97952
                        nm->mkNode(rel,
82
48976
                                   nm->mkNode(Kind::MULT, mult, lhs),
83
122440
                                   nm->mkNode(Kind::MULT, mult, rhs)));
84
    }
85
27504
    case PfRule::ARITH_MULT_NEG:
86
    {
87
27504
      Assert(children.empty());
88
27504
      Assert(args.size() == 2);
89
55008
      Node mult = args[0];
90
27504
      Kind rel = args[1].getKind();
91
27504
      Assert(rel == Kind::EQUAL || rel == Kind::DISTINCT || rel == Kind::LT
92
             || rel == Kind::LEQ || rel == Kind::GT || rel == Kind::GEQ);
93
27504
      Kind rel_inv = (rel == Kind::DISTINCT ? rel : reverseRelationKind(rel));
94
55008
      Node lhs = args[1][0];
95
55008
      Node rhs = args[1][1];
96
      return nm->mkNode(Kind::IMPLIES,
97
137520
                        nm->mkAnd(std::vector<Node>{
98
82512
                            nm->mkNode(Kind::LT, mult, zero), args[1]}),
99
110016
                        nm->mkNode(rel_inv,
100
55008
                                   nm->mkNode(Kind::MULT, mult, lhs),
101
137520
                                   nm->mkNode(Kind::MULT, mult, rhs)));
102
    }
103
9588
    case PfRule::ARITH_SUM_UB:
104
    {
105
9588
      if (children.size() < 2)
106
      {
107
        return Node::null();
108
      }
109
110
      // Whether a strict inequality is in the sum.
111
9588
      bool strict = false;
112
19176
      NodeBuilder leftSum(Kind::PLUS);
113
19176
      NodeBuilder rightSum(Kind::PLUS);
114
61058
      for (size_t i = 0; i < children.size(); ++i)
115
      {
116
        // Adjust strictness
117
51470
        switch (children[i].getKind())
118
        {
119
8920
          case Kind::LT:
120
          {
121
8920
            strict = true;
122
8920
            break;
123
          }
124
42550
          case Kind::LEQ:
125
          case Kind::EQUAL:
126
          {
127
42550
            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
51470
        leftSum << children[i][0];
137
51470
        rightSum << children[i][1];
138
      }
139
      Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ,
140
19176
                          leftSum.constructNode(),
141
38352
                          rightSum.constructNode());
142
9588
      return r;
143
    }
144
78106
    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
78106
      Assert(children.size() == args.size());
169
78106
      if (children.size() < 2)
170
      {
171
        return Node::null();
172
      }
173
174
      // Whether a strict inequality is in the sum.
175
78106
      bool strict = false;
176
156212
      NodeBuilder leftSum(Kind::PLUS);
177
156212
      NodeBuilder rightSum(Kind::PLUS);
178
334587
      for (size_t i = 0; i < children.size(); ++i)
179
      {
180
512962
        Rational scalar = args[i].getConst<Rational>();
181
256481
        if (scalar == 0)
182
        {
183
          Debug("arith::pf::check") << "Error: zero scalar" << std::endl;
184
          return Node::null();
185
        }
186
187
        // Adjust strictness
188
256481
        switch (children[i].getKind())
189
        {
190
49149
          case Kind::GT:
191
          case Kind::LT:
192
          {
193
49149
            strict = true;
194
49149
            break;
195
          }
196
207332
          case Kind::GEQ:
197
          case Kind::LEQ:
198
          case Kind::EQUAL:
199
          {
200
207332
            break;
201
          }
202
          default:
203
          {
204
            Debug("arith::pf::check")
205
                << "Bad kind: " << children[i].getKind() << std::endl;
206
          }
207
        }
208
        // Check sign
209
256481
        switch (children[i].getKind())
210
        {
211
72301
          case Kind::GT:
212
          case Kind::GEQ:
213
          {
214
72301
            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
72301
            break;
222
          }
223
78453
          case Kind::LEQ:
224
          case Kind::LT:
225
          {
226
78453
            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
78453
            break;
234
          }
235
105727
          case Kind::EQUAL:
236
          {
237
105727
            break;
238
          }
239
          default:
240
          {
241
            Debug("arith::pf::check")
242
                << "Bad kind: " << children[i].getKind() << std::endl;
243
          }
244
        }
245
1282405
        leftSum << nm->mkNode(
246
1282405
            Kind::MULT, nm->mkConst<Rational>(scalar), children[i][0]);
247
1282405
        rightSum << nm->mkNode(
248
1282405
            Kind::MULT, nm->mkConst<Rational>(scalar), children[i][1]);
249
      }
250
      Node r = nm->mkNode(strict ? Kind::LT : Kind::LEQ,
251
156212
                          leftSum.constructNode(),
252
312424
                          rightSum.constructNode());
253
78106
      return r;
254
    }
255
1461
    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
2922
      if (children.size() != 1
263
1461
          || (children[0].getKind() != Kind::GT
264
              && children[0].getKind() != Kind::GEQ)
265
2922
          || !children[0][0].getType().isInteger()
266
4383
          || 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
2922
        Rational originalBound = children[0][1].getConst<Rational>();
274
2922
        Rational newBound = leastIntGreaterThan(originalBound);
275
2922
        Node rational = nm->mkConst<Rational>(newBound);
276
1461
        return nm->mkNode(kind::GEQ, children[0][0], rational);
277
      }
278
    }
279
63912
    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
127824
      if (children.size() != 1
288
63912
          || (children[0].getKind() != Kind::LT
289
              && children[0].getKind() != Kind::LEQ)
290
127824
          || !children[0][0].getType().isInteger()
291
191736
          || 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
127824
        Rational originalBound = children[0][1].getConst<Rational>();
299
127824
        Rational newBound = greatestIntLessThan(originalBound);
300
127824
        Node rational = nm->mkConst<Rational>(newBound);
301
63912
        return nm->mkNode(kind::LEQ, children[0][0], rational);
302
      }
303
    }
304
45407
    case PfRule::ARITH_TRICHOTOMY:
305
    {
306
90814
      Node a = negateProofLiteral(children[0]);
307
90814
      Node b = negateProofLiteral(children[1]);
308
90814
      Node c = args[0];
309
45407
      if (a[0] == b[0] && b[0] == c[0] && a[1] == b[1] && b[1] == c[1])
310
      {
311
90814
        std::set<Kind> cmps;
312
45407
        cmps.insert(a.getKind());
313
45407
        cmps.insert(b.getKind());
314
45407
        cmps.insert(c.getKind());
315
45407
        if (cmps.count(Kind::EQUAL) == 0)
316
        {
317
          Debug("arith::pf::check") << "Error: No = " << std::endl;
318
          return Node::null();
319
        }
320
45407
        if (cmps.count(Kind::GT) == 0)
321
        {
322
          Debug("arith::pf::check") << "Error: No > " << std::endl;
323
          return Node::null();
324
        }
325
45407
        if (cmps.count(Kind::LT) == 0)
326
        {
327
          Debug("arith::pf::check") << "Error: No < " << std::endl;
328
          return Node::null();
329
        }
330
45407
        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
29322
}  // namespace cvc5