GCC Code Coverage Report
Directory: . Exec Total Coverage
File: test/unit/theory/theory_arith_cad_white.cpp Lines: 203 220 92.3 %
Date: 2021-08-01 Branches: 612 1468 41.7 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Gereon Kremer
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
 * Unit tests for the CAD module for nonlinear arithmetic.
14
 */
15
16
#ifdef CVC5_USE_POLY
17
18
#include <poly/polyxx.h>
19
20
#include <iostream>
21
#include <memory>
22
#include <vector>
23
24
#include "test_smt.h"
25
#include "theory/arith/nl/cad/cdcac.h"
26
#include "theory/arith/nl/cad/lazard_evaluation.h"
27
#include "theory/arith/nl/cad/projections.h"
28
#include "theory/arith/nl/cad_solver.h"
29
#include "theory/arith/nl/nl_lemma_utils.h"
30
#include "theory/arith/nl/poly_conversion.h"
31
#include "theory/arith/theory_arith.h"
32
#include "theory/quantifiers/extended_rewrite.h"
33
#include "theory/theory.h"
34
#include "theory/theory_engine.h"
35
#include "util/poly_util.h"
36
37
namespace cvc5::test {
38
39
using namespace cvc5;
40
using namespace cvc5::theory;
41
using namespace cvc5::theory::arith;
42
using namespace cvc5::theory::arith::nl;
43
44
NodeManager* nodeManager;
45
48
class TestTheoryWhiteArithCAD : public TestSmt
46
{
47
 protected:
48
24
  void SetUp() override
49
  {
50
24
    TestSmt::SetUp();
51
24
    d_realType.reset(new TypeNode(d_nodeManager->realType()));
52
24
    d_intType.reset(new TypeNode(d_nodeManager->integerType()));
53
24
    Trace.on("cad-check");
54
24
    nodeManager = d_nodeManager.get();
55
24
  }
56
57
36
  Node dummy(int i) const { return d_nodeManager->mkConst(Rational(i)); }
58
59
  Theory::Effort d_level = Theory::EFFORT_FULL;
60
  std::unique_ptr<TypeNode> d_realType;
61
  std::unique_ptr<TypeNode> d_intType;
62
  const Rational d_zero = 0;
63
  const Rational d_one = 1;
64
};
65
66
10
poly::AlgebraicNumber get_ran(std::initializer_list<long> init,
67
                              int lower,
68
                              int upper)
69
{
70
20
  return poly::AlgebraicNumber(poly::UPolynomial(init),
71
30
                               poly::DyadicInterval(lower, upper));
72
}
73
74
24
Node operator==(const Node& a, const Node& b)
75
{
76
24
  return nodeManager->mkNode(Kind::EQUAL, a, b);
77
}
78
Node operator>=(const Node& a, const Node& b)
79
{
80
  return nodeManager->mkNode(Kind::GEQ, a, b);
81
}
82
12
Node operator+(const Node& a, const Node& b)
83
{
84
12
  return nodeManager->mkNode(Kind::PLUS, a, b);
85
}
86
32
Node operator*(const Node& a, const Node& b)
87
{
88
32
  return nodeManager->mkNode(Kind::NONLINEAR_MULT, a, b);
89
}
90
4
Node operator!(const Node& a) { return nodeManager->mkNode(Kind::NOT, a); }
91
42
Node make_real_variable(const std::string& s)
92
{
93
  return nodeManager->mkSkolem(
94
42
      s, nodeManager->realType(), "", NodeManager::SKOLEM_EXACT_NAME);
95
}
96
Node make_int_variable(const std::string& s)
97
{
98
  return nodeManager->mkSkolem(
99
      s, nodeManager->integerType(), "", NodeManager::SKOLEM_EXACT_NAME);
100
}
101
102
22
TEST_F(TestTheoryWhiteArithCAD, test_univariate_isolation)
103
{
104
4
  poly::UPolynomial poly({-2, 2, 3, -3, -1, 1});
105
4
  auto roots = poly::isolate_real_roots(poly);
106
107
2
  EXPECT_TRUE(roots.size() == 4);
108
2
  EXPECT_TRUE(roots[0] == get_ran({-2, 0, 1}, -2, -1));
109
2
  EXPECT_TRUE(roots[1] == poly::Integer(-1));
110
2
  EXPECT_TRUE(roots[2] == poly::Integer(1));
111
2
  EXPECT_TRUE(roots[3] == get_ran({-2, 0, 1}, 1, 2));
112
2
}
113
114
22
TEST_F(TestTheoryWhiteArithCAD, test_multivariate_isolation)
115
{
116
2
  poly::Variable x("x");
117
2
  poly::Variable y("y");
118
2
  poly::Variable z("z");
119
120
4
  poly::Assignment a;
121
2
  a.set(x, get_ran({-2, 0, 1}, 1, 2));
122
2
  a.set(y, get_ran({-2, 0, 0, 0, 1}, 1, 2));
123
124
4
  poly::Polynomial poly = (y * y + x) - z;
125
126
4
  auto roots = poly::isolate_real_roots(poly, a);
127
128
2
  EXPECT_TRUE(roots.size() == 1);
129
2
  EXPECT_TRUE(roots[0] == get_ran({-8, 0, 1}, 2, 3));
130
2
}
131
132
22
TEST_F(TestTheoryWhiteArithCAD, test_univariate_factorization)
133
{
134
4
  poly::UPolynomial poly({-24, 44, -18, -1, 1, -3, 1});
135
136
4
  auto factors = square_free_factors(poly);
137
2
  std::sort(factors.begin(), factors.end());
138
2
  EXPECT_EQ(factors[0], poly::UPolynomial({-1, 1}));
139
2
  EXPECT_EQ(factors[1], poly::UPolynomial({-24, -4, -2, -1, 1}));
140
2
}
141
142
22
TEST_F(TestTheoryWhiteArithCAD, test_projection)
143
{
144
  // Gereon's thesis, Ex 5.1
145
2
  poly::Variable x("x");
146
2
  poly::Variable y("y");
147
148
4
  poly::Polynomial p = (y + 1) * (y + 1) - x * x * x + 3 * x - 2;
149
4
  poly::Polynomial q = (x + 1) * y - 3;
150
151
4
  auto res = cad::projectionMcCallum({p, q});
152
2
  std::sort(res.begin(), res.end());
153
2
  EXPECT_EQ(res[0], x - 1);
154
2
  EXPECT_EQ(res[1], x + 1);
155
2
  EXPECT_EQ(res[2], x + 2);
156
2
  EXPECT_EQ(res[3], x * x * x - 3 * x + 1);
157
2
  EXPECT_EQ(res[4],
158
            x * x * x * x * x + 2 * x * x * x * x - 2 * x * x * x - 5 * x * x
159
2
                - 7 * x - 14);
160
2
}
161
162
poly::Polynomial up_to_poly(const poly::UPolynomial& p, poly::Variable var)
163
{
164
  poly::Polynomial res;
165
  poly::Polynomial mult = 1;
166
  for (const auto& coeff : coefficients(p))
167
  {
168
    if (!is_zero(coeff))
169
    {
170
      res += mult * coeff;
171
    }
172
    mult *= var;
173
  }
174
  return res;
175
}
176
177
22
TEST_F(TestTheoryWhiteArithCAD, lazard_simp)
178
{
179
4
  Node a = d_nodeManager->mkVar(*d_realType);
180
4
  Node c = d_nodeManager->mkVar(*d_realType);
181
16
  Node orig = d_nodeManager->mkAnd(std::vector<Node>{
182
4
      d_nodeManager->mkNode(Kind::EQUAL, a, d_nodeManager->mkConst(d_zero)),
183
      d_nodeManager->mkNode(
184
          Kind::EQUAL,
185
10
          d_nodeManager->mkNode(
186
              Kind::PLUS,
187
4
              d_nodeManager->mkNode(Kind::NONLINEAR_MULT, a, c),
188
4
              d_nodeManager->mkConst(d_one)),
189
14
          d_nodeManager->mkConst(d_zero))});
190
191
  {
192
4
    Node rewritten = Rewriter::rewrite(orig);
193
2
    EXPECT_NE(rewritten, d_nodeManager->mkConst(false));
194
  }
195
  {
196
4
    quantifiers::ExtendedRewriter extrew;
197
4
    Node rewritten = extrew.extendedRewrite(orig);
198
2
    EXPECT_EQ(rewritten, d_nodeManager->mkConst(false));
199
  }
200
2
}
201
202
#ifdef CVC5_USE_COCOA
203
TEST_F(TestTheoryWhiteArithCAD, lazard_eval)
204
{
205
  poly::Variable x("x");
206
  poly::Variable y("y");
207
  poly::Variable z("z");
208
  poly::Variable f("f");
209
  poly::AlgebraicNumber ax = get_ran({-2, 0, 1}, 1, 2);
210
  poly::AlgebraicNumber ay = get_ran({-2, 0, 0, 0, 1}, 1, 2);
211
  poly::AlgebraicNumber az = get_ran({-3, 0, 1}, 1, 2);
212
213
  cad::LazardEvaluation lazard;
214
  lazard.add(x, ax);
215
  lazard.add(y, ay);
216
  lazard.add(z, az);
217
218
  poly::Polynomial q = (x * x - 2) * (y * y * y * y - 2) * z * f;
219
  lazard.addFreeVariable(f);
220
  auto qred = lazard.reducePolynomial(q);
221
  EXPECT_EQ(qred, std::vector<poly::Polynomial>{f});
222
}
223
#endif
224
225
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_1)
226
{
227
4
  cad::CDCAC cac(nullptr, nullptr, {});
228
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
229
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
230
231
4
  cac.getConstraints().addConstraint(
232
4
      4 * y - x * x + 4, poly::SignCondition::LT, dummy(1));
233
4
  cac.getConstraints().addConstraint(
234
4
      4 * y - 4 + (x - 1) * (x - 1), poly::SignCondition::GT, dummy(2));
235
4
  cac.getConstraints().addConstraint(
236
4
      4 * y - x - 2, poly::SignCondition::GT, dummy(3));
237
238
2
  cac.computeVariableOrdering();
239
240
4
  auto cover = cac.getUnsatCover();
241
2
  EXPECT_TRUE(cover.empty());
242
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
243
2
}
244
245
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_2)
246
{
247
4
  cad::CDCAC cac(nullptr, nullptr, {});
248
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
249
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
250
251
4
  cac.getConstraints().addConstraint(y - pow(-x - 3, 11) + pow(-x - 3, 10) + 1,
252
                                     poly::SignCondition::GT,
253
4
                                     dummy(1));
254
4
  cac.getConstraints().addConstraint(
255
4
      2 * y - x + 2, poly::SignCondition::LT, dummy(2));
256
4
  cac.getConstraints().addConstraint(
257
4
      2 * y - 1 + x * x, poly::SignCondition::GT, dummy(3));
258
4
  cac.getConstraints().addConstraint(
259
4
      3 * y + x + 2, poly::SignCondition::LT, dummy(4));
260
4
  cac.getConstraints().addConstraint(
261
4
      y * y * y - pow(x - 2, 11) + pow(x - 2, 10) + 1,
262
      poly::SignCondition::GT,
263
4
      dummy(5));
264
265
2
  cac.computeVariableOrdering();
266
267
4
  auto cover = cac.getUnsatCover();
268
2
  EXPECT_TRUE(!cover.empty());
269
4
  auto nodes = cad::collectConstraints(cover);
270
4
  std::vector<Node> ref{dummy(1), dummy(2), dummy(3), dummy(4), dummy(5)};
271
2
  EXPECT_EQ(nodes, ref);
272
2
}
273
274
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_3)
275
{
276
4
  cad::CDCAC cac(nullptr, nullptr, {});
277
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
278
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
279
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
280
281
4
  cac.getConstraints().addConstraint(
282
4
      x * x + y * y + z * z - 1, poly::SignCondition::LT, dummy(1));
283
4
  cac.getConstraints().addConstraint(
284
4
      4 * x * x + (2 * y - 3) * (2 * y - 3) + 4 * z * z - 4,
285
      poly::SignCondition::LT,
286
4
      dummy(2));
287
288
2
  cac.computeVariableOrdering();
289
290
4
  auto cover = cac.getUnsatCover();
291
2
  EXPECT_TRUE(cover.empty());
292
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
293
2
}
294
295
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_4)
296
{
297
4
  cad::CDCAC cac(nullptr, nullptr, {});
298
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
299
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
300
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
301
302
4
  cac.getConstraints().addConstraint(
303
4
      -z * z + y * y + x * x - 25, poly::SignCondition::GT, dummy(1));
304
4
  cac.getConstraints().addConstraint(
305
4
      (y - x - 6) * z * z - 9 * y * y + x * x - 1,
306
      poly::SignCondition::GT,
307
4
      dummy(2));
308
4
  cac.getConstraints().addConstraint(
309
4
      y * y - 100, poly::SignCondition::LT, dummy(3));
310
311
2
  cac.computeVariableOrdering();
312
313
4
  auto cover = cac.getUnsatCover();
314
2
  EXPECT_TRUE(cover.empty());
315
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
316
2
}
317
318
4
void test_delta(const std::vector<Node>& a)
319
{
320
8
  cad::CDCAC cac(nullptr, nullptr, {});
321
4
  cac.reset();
322
28
  for (const Node& n : a)
323
  {
324
24
    cac.getConstraints().addConstraint(n);
325
  }
326
4
  cac.computeVariableOrdering();
327
328
  // Do full theory check here
329
330
8
  auto covering = cac.getUnsatCover();
331
4
  if (covering.empty())
332
  {
333
4
    std::cout << "SAT: " << cac.getModel() << std::endl;
334
  }
335
  else
336
  {
337
    auto mis = cad::collectConstraints(covering);
338
    std::cout << "Collected MIS: " << mis << std::endl;
339
    Assert(!mis.empty()) << "Infeasible subset can not be empty";
340
    Node lem = NodeManager::currentNM()->mkAnd(mis).negate();
341
    Notice() << "UNSAT with MIS: " << lem << std::endl;
342
  }
343
4
}
344
345
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_one)
346
{
347
4
  std::vector<Node> a;
348
4
  Node zero = d_nodeManager->mkConst(Rational(0));
349
4
  Node one = d_nodeManager->mkConst(Rational(1));
350
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
351
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
352
4
  Node g = make_real_variable("g");
353
4
  Node l = make_real_variable("l");
354
4
  Node q = make_real_variable("q");
355
4
  Node s = make_real_variable("s");
356
4
  Node u = make_real_variable("u");
357
358
2
  a.emplace_back(l == mone);
359
2
  a.emplace_back(!(g * s == zero));
360
2
  a.emplace_back(q * s == zero);
361
2
  a.emplace_back(u == zero);
362
2
  a.emplace_back(q == (one + (fifth * g * s)));
363
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
364
365
2
  test_delta(a);
366
2
}
367
368
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_two)
369
{
370
4
  std::vector<Node> a;
371
4
  Node zero = d_nodeManager->mkConst(Rational(0));
372
4
  Node one = d_nodeManager->mkConst(Rational(1));
373
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
374
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
375
4
  Node g = make_real_variable("g");
376
4
  Node l = make_real_variable("l");
377
4
  Node q = make_real_variable("q");
378
4
  Node s = make_real_variable("s");
379
4
  Node u = make_real_variable("u");
380
381
2
  a.emplace_back(l == mone);
382
2
  a.emplace_back(!(g * s == zero));
383
2
  a.emplace_back(u == zero);
384
2
  a.emplace_back(q * s == zero);
385
2
  a.emplace_back(q == (one + (fifth * g * s)));
386
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
387
388
2
  test_delta(a);
389
2
}
390
391
22
TEST_F(TestTheoryWhiteArithCAD, test_ran_conversion)
392
{
393
  RealAlgebraicNumber ran(
394
4
      std::vector<Rational>({-2, 0, 1}), Rational(1, 3), Rational(7, 3));
395
  {
396
4
    Node x = make_real_variable("x");
397
4
    Node n = nl::ran_to_node(ran, x);
398
4
    RealAlgebraicNumber back = nl::node_to_ran(n, x);
399
2
    EXPECT_TRUE(ran == back);
400
  }
401
2
}
402
267898
}  // namespace cvc5::test
403
404
#endif