GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/proof_checker.cpp Lines: 123 183 67.2 %
Date: 2021-11-07 Branches: 253 912 27.7 %

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