GCC Code Coverage Report
Directory: . Exec Total Coverage
File: test/unit/theory/theory_arith_cad_white.cpp Lines: 212 229 92.6 %
Date: 2021-09-17 Branches: 625 1494 41.8 %

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/rewriter.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
    Node rewritten = Rewriter::callExtendedRewrite(orig);
197
2
    EXPECT_EQ(rewritten, d_nodeManager->mkConst(false));
198
  }
199
2
}
200
201
#ifdef CVC5_USE_COCOA
202
TEST_F(TestTheoryWhiteArithCAD, lazard_eval)
203
{
204
  poly::Variable x("x");
205
  poly::Variable y("y");
206
  poly::Variable z("z");
207
  poly::Variable f("f");
208
  poly::AlgebraicNumber ax = get_ran({-2, 0, 1}, 1, 2);
209
  poly::AlgebraicNumber ay = get_ran({-2, 0, 0, 0, 1}, 1, 2);
210
  poly::AlgebraicNumber az = get_ran({-3, 0, 1}, 1, 2);
211
212
  cad::LazardEvaluation lazard;
213
  lazard.add(x, ax);
214
  lazard.add(y, ay);
215
  lazard.add(z, az);
216
217
  poly::Polynomial q = (x * x - 2) * (y * y * y * y - 2) * z * f;
218
  lazard.addFreeVariable(f);
219
  auto qred = lazard.reducePolynomial(q);
220
  EXPECT_EQ(qred, std::vector<poly::Polynomial>{f});
221
}
222
#endif
223
224
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_1)
225
{
226
4
  Options opts;
227
4
  Env env(NodeManager::currentNM(), &opts);
228
4
  cad::CDCAC cac(env, {});
229
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
230
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
231
232
4
  cac.getConstraints().addConstraint(
233
4
      4 * y - x * x + 4, poly::SignCondition::LT, dummy(1));
234
4
  cac.getConstraints().addConstraint(
235
4
      4 * y - 4 + (x - 1) * (x - 1), poly::SignCondition::GT, dummy(2));
236
4
  cac.getConstraints().addConstraint(
237
4
      4 * y - x - 2, poly::SignCondition::GT, dummy(3));
238
239
2
  cac.computeVariableOrdering();
240
241
4
  auto cover = cac.getUnsatCover();
242
2
  EXPECT_TRUE(cover.empty());
243
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
244
2
}
245
246
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_2)
247
{
248
4
  Options opts;
249
4
  Env env(NodeManager::currentNM(), &opts);
250
4
  cad::CDCAC cac(env, {});
251
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
252
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
253
254
4
  cac.getConstraints().addConstraint(y - pow(-x - 3, 11) + pow(-x - 3, 10) + 1,
255
                                     poly::SignCondition::GT,
256
4
                                     dummy(1));
257
4
  cac.getConstraints().addConstraint(
258
4
      2 * y - x + 2, poly::SignCondition::LT, dummy(2));
259
4
  cac.getConstraints().addConstraint(
260
4
      2 * y - 1 + x * x, poly::SignCondition::GT, dummy(3));
261
4
  cac.getConstraints().addConstraint(
262
4
      3 * y + x + 2, poly::SignCondition::LT, dummy(4));
263
4
  cac.getConstraints().addConstraint(
264
4
      y * y * y - pow(x - 2, 11) + pow(x - 2, 10) + 1,
265
      poly::SignCondition::GT,
266
4
      dummy(5));
267
268
2
  cac.computeVariableOrdering();
269
270
4
  auto cover = cac.getUnsatCover();
271
2
  EXPECT_TRUE(!cover.empty());
272
4
  auto nodes = cad::collectConstraints(cover);
273
4
  std::vector<Node> ref{dummy(1), dummy(2), dummy(3), dummy(4), dummy(5)};
274
2
  EXPECT_EQ(nodes, ref);
275
2
}
276
277
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_3)
278
{
279
4
  Options opts;
280
4
  Env env(NodeManager::currentNM(), &opts);
281
4
  cad::CDCAC cac(env, {});
282
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
283
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
284
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
285
286
4
  cac.getConstraints().addConstraint(
287
4
      x * x + y * y + z * z - 1, poly::SignCondition::LT, dummy(1));
288
4
  cac.getConstraints().addConstraint(
289
4
      4 * x * x + (2 * y - 3) * (2 * y - 3) + 4 * z * z - 4,
290
      poly::SignCondition::LT,
291
4
      dummy(2));
292
293
2
  cac.computeVariableOrdering();
294
295
4
  auto cover = cac.getUnsatCover();
296
2
  EXPECT_TRUE(cover.empty());
297
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
298
2
}
299
300
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_4)
301
{
302
4
  Options opts;
303
4
  Env env(NodeManager::currentNM(), &opts);
304
4
  cad::CDCAC cac(env, {});
305
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
306
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
307
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
308
309
4
  cac.getConstraints().addConstraint(
310
4
      -z * z + y * y + x * x - 25, poly::SignCondition::GT, dummy(1));
311
4
  cac.getConstraints().addConstraint(
312
4
      (y - x - 6) * z * z - 9 * y * y + x * x - 1,
313
      poly::SignCondition::GT,
314
4
      dummy(2));
315
4
  cac.getConstraints().addConstraint(
316
4
      y * y - 100, poly::SignCondition::LT, dummy(3));
317
318
2
  cac.computeVariableOrdering();
319
320
4
  auto cover = cac.getUnsatCover();
321
2
  EXPECT_TRUE(cover.empty());
322
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
323
2
}
324
325
4
void test_delta(const std::vector<Node>& a)
326
{
327
8
  Options opts;
328
8
  Env env(NodeManager::currentNM(), &opts);
329
8
  cad::CDCAC cac(env, {});
330
4
  cac.reset();
331
28
  for (const Node& n : a)
332
  {
333
24
    cac.getConstraints().addConstraint(n);
334
  }
335
4
  cac.computeVariableOrdering();
336
337
  // Do full theory check here
338
339
8
  auto covering = cac.getUnsatCover();
340
4
  if (covering.empty())
341
  {
342
4
    std::cout << "SAT: " << cac.getModel() << std::endl;
343
  }
344
  else
345
  {
346
    auto mis = cad::collectConstraints(covering);
347
    std::cout << "Collected MIS: " << mis << std::endl;
348
    Assert(!mis.empty()) << "Infeasible subset can not be empty";
349
    Node lem = NodeManager::currentNM()->mkAnd(mis).negate();
350
    Notice() << "UNSAT with MIS: " << lem << std::endl;
351
  }
352
4
}
353
354
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_one)
355
{
356
4
  std::vector<Node> a;
357
4
  Node zero = d_nodeManager->mkConst(Rational(0));
358
4
  Node one = d_nodeManager->mkConst(Rational(1));
359
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
360
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
361
4
  Node g = make_real_variable("g");
362
4
  Node l = make_real_variable("l");
363
4
  Node q = make_real_variable("q");
364
4
  Node s = make_real_variable("s");
365
4
  Node u = make_real_variable("u");
366
367
2
  a.emplace_back(l == mone);
368
2
  a.emplace_back(!(g * s == zero));
369
2
  a.emplace_back(q * s == zero);
370
2
  a.emplace_back(u == zero);
371
2
  a.emplace_back(q == (one + (fifth * g * s)));
372
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
373
374
2
  test_delta(a);
375
2
}
376
377
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_two)
378
{
379
4
  std::vector<Node> a;
380
4
  Node zero = d_nodeManager->mkConst(Rational(0));
381
4
  Node one = d_nodeManager->mkConst(Rational(1));
382
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
383
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
384
4
  Node g = make_real_variable("g");
385
4
  Node l = make_real_variable("l");
386
4
  Node q = make_real_variable("q");
387
4
  Node s = make_real_variable("s");
388
4
  Node u = make_real_variable("u");
389
390
2
  a.emplace_back(l == mone);
391
2
  a.emplace_back(!(g * s == zero));
392
2
  a.emplace_back(u == zero);
393
2
  a.emplace_back(q * s == zero);
394
2
  a.emplace_back(q == (one + (fifth * g * s)));
395
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
396
397
2
  test_delta(a);
398
2
}
399
400
22
TEST_F(TestTheoryWhiteArithCAD, test_ran_conversion)
401
{
402
  RealAlgebraicNumber ran(
403
4
      std::vector<Rational>({-2, 0, 1}), Rational(1, 3), Rational(7, 3));
404
  {
405
4
    Node x = make_real_variable("x");
406
4
    Node n = nl::ran_to_node(ran, x);
407
4
    RealAlgebraicNumber back = nl::node_to_ran(n, x);
408
2
    EXPECT_TRUE(ran == back);
409
  }
410
2
}
411
268294
}  // namespace cvc5::test
412
413
#endif