GCC Code Coverage Report
Directory: . Exec Total Coverage
File: test/unit/theory/theory_arith_cad_white.cpp Lines: 214 232 92.2 %
Date: 2021-11-07 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;
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
42
  SkolemManager* sm = nodeManager->getSkolemManager();
94
  return sm->mkDummySkolem(
95
42
      s, nodeManager->realType(), "", SkolemManager::SKOLEM_EXACT_NAME);
96
}
97
Node make_int_variable(const std::string& s)
98
{
99
  SkolemManager* sm = nodeManager->getSkolemManager();
100
  return sm->mkDummySkolem(
101
      s, nodeManager->integerType(), "", SkolemManager::SKOLEM_EXACT_NAME);
102
}
103
104
22
TEST_F(TestTheoryWhiteArithCAD, test_univariate_isolation)
105
{
106
4
  poly::UPolynomial poly({-2, 2, 3, -3, -1, 1});
107
4
  auto roots = poly::isolate_real_roots(poly);
108
109
2
  EXPECT_TRUE(roots.size() == 4);
110
2
  EXPECT_TRUE(roots[0] == get_ran({-2, 0, 1}, -2, -1));
111
2
  EXPECT_TRUE(roots[1] == poly::Integer(-1));
112
2
  EXPECT_TRUE(roots[2] == poly::Integer(1));
113
2
  EXPECT_TRUE(roots[3] == get_ran({-2, 0, 1}, 1, 2));
114
2
}
115
116
22
TEST_F(TestTheoryWhiteArithCAD, test_multivariate_isolation)
117
{
118
2
  poly::Variable x("x");
119
2
  poly::Variable y("y");
120
2
  poly::Variable z("z");
121
122
4
  poly::Assignment a;
123
2
  a.set(x, get_ran({-2, 0, 1}, 1, 2));
124
2
  a.set(y, get_ran({-2, 0, 0, 0, 1}, 1, 2));
125
126
4
  poly::Polynomial poly = (y * y + x) - z;
127
128
4
  auto roots = poly::isolate_real_roots(poly, a);
129
130
2
  EXPECT_TRUE(roots.size() == 1);
131
2
  EXPECT_TRUE(roots[0] == get_ran({-8, 0, 1}, 2, 3));
132
2
}
133
134
22
TEST_F(TestTheoryWhiteArithCAD, test_univariate_factorization)
135
{
136
4
  poly::UPolynomial poly({-24, 44, -18, -1, 1, -3, 1});
137
138
4
  auto factors = square_free_factors(poly);
139
2
  std::sort(factors.begin(), factors.end());
140
2
  EXPECT_EQ(factors[0], poly::UPolynomial({-1, 1}));
141
2
  EXPECT_EQ(factors[1], poly::UPolynomial({-24, -4, -2, -1, 1}));
142
2
}
143
144
22
TEST_F(TestTheoryWhiteArithCAD, test_projection)
145
{
146
  // Gereon's thesis, Ex 5.1
147
2
  poly::Variable x("x");
148
2
  poly::Variable y("y");
149
150
4
  poly::Polynomial p = (y + 1) * (y + 1) - x * x * x + 3 * x - 2;
151
4
  poly::Polynomial q = (x + 1) * y - 3;
152
153
4
  auto res = cad::projectionMcCallum({p, q});
154
2
  std::sort(res.begin(), res.end());
155
2
  EXPECT_EQ(res[0], x - 1);
156
2
  EXPECT_EQ(res[1], x + 1);
157
2
  EXPECT_EQ(res[2], x + 2);
158
2
  EXPECT_EQ(res[3], x * x * x - 3 * x + 1);
159
2
  EXPECT_EQ(res[4],
160
            x * x * x * x * x + 2 * x * x * x * x - 2 * x * x * x - 5 * x * x
161
2
                - 7 * x - 14);
162
2
}
163
164
poly::Polynomial up_to_poly(const poly::UPolynomial& p, poly::Variable var)
165
{
166
  poly::Polynomial res;
167
  poly::Polynomial mult = 1;
168
  for (const auto& coeff : coefficients(p))
169
  {
170
    if (!is_zero(coeff))
171
    {
172
      res += mult * coeff;
173
    }
174
    mult *= var;
175
  }
176
  return res;
177
}
178
179
22
TEST_F(TestTheoryWhiteArithCAD, lazard_simp)
180
{
181
4
  Node a = d_nodeManager->mkVar(*d_realType);
182
4
  Node c = d_nodeManager->mkVar(*d_realType);
183
16
  Node orig = d_nodeManager->mkAnd(std::vector<Node>{
184
4
      d_nodeManager->mkNode(Kind::EQUAL, a, d_nodeManager->mkConst(d_zero)),
185
2
      d_nodeManager->mkNode(
186
          Kind::EQUAL,
187
10
          d_nodeManager->mkNode(
188
              Kind::PLUS,
189
4
              d_nodeManager->mkNode(Kind::NONLINEAR_MULT, a, c),
190
4
              d_nodeManager->mkConst(d_one)),
191
12
          d_nodeManager->mkConst(d_zero))});
192
193
  {
194
4
    Node rewritten = Rewriter::rewrite(orig);
195
2
    EXPECT_NE(rewritten, d_nodeManager->mkConst(false));
196
  }
197
  {
198
4
    Node rewritten = Rewriter::callExtendedRewrite(orig);
199
2
    EXPECT_EQ(rewritten, d_nodeManager->mkConst(false));
200
  }
201
2
}
202
203
#ifdef CVC5_USE_COCOA
204
TEST_F(TestTheoryWhiteArithCAD, lazard_eval)
205
{
206
  poly::Variable x("x");
207
  poly::Variable y("y");
208
  poly::Variable z("z");
209
  poly::Variable f("f");
210
  poly::AlgebraicNumber ax = get_ran({-2, 0, 1}, 1, 2);
211
  poly::AlgebraicNumber ay = get_ran({-2, 0, 0, 0, 1}, 1, 2);
212
  poly::AlgebraicNumber az = get_ran({-3, 0, 1}, 1, 2);
213
214
  cad::LazardEvaluation lazard;
215
  lazard.add(x, ax);
216
  lazard.add(y, ay);
217
  lazard.add(z, az);
218
219
  poly::Polynomial q = (x * x - 2) * (y * y * y * y - 2) * z * f;
220
  lazard.addFreeVariable(f);
221
  auto qred = lazard.reducePolynomial(q);
222
  EXPECT_EQ(qred, std::vector<poly::Polynomial>{f});
223
}
224
#endif
225
226
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_1)
227
{
228
4
  Options opts;
229
4
  Env env(NodeManager::currentNM(), &opts);
230
4
  cad::CDCAC cac(env, {});
231
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
232
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
233
234
4
  cac.getConstraints().addConstraint(
235
4
      4 * y - x * x + 4, poly::SignCondition::LT, dummy(1));
236
4
  cac.getConstraints().addConstraint(
237
4
      4 * y - 4 + (x - 1) * (x - 1), poly::SignCondition::GT, dummy(2));
238
4
  cac.getConstraints().addConstraint(
239
4
      4 * y - x - 2, poly::SignCondition::GT, dummy(3));
240
241
2
  cac.computeVariableOrdering();
242
243
4
  auto cover = cac.getUnsatCover();
244
2
  EXPECT_TRUE(cover.empty());
245
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
246
2
}
247
248
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_2)
249
{
250
4
  Options opts;
251
4
  Env env(NodeManager::currentNM(), &opts);
252
4
  cad::CDCAC cac(env, {});
253
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
254
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
255
256
4
  cac.getConstraints().addConstraint(y - pow(-x - 3, 11) + pow(-x - 3, 10) + 1,
257
                                     poly::SignCondition::GT,
258
4
                                     dummy(1));
259
4
  cac.getConstraints().addConstraint(
260
4
      2 * y - x + 2, poly::SignCondition::LT, dummy(2));
261
4
  cac.getConstraints().addConstraint(
262
4
      2 * y - 1 + x * x, poly::SignCondition::GT, dummy(3));
263
4
  cac.getConstraints().addConstraint(
264
4
      3 * y + x + 2, poly::SignCondition::LT, dummy(4));
265
4
  cac.getConstraints().addConstraint(
266
4
      y * y * y - pow(x - 2, 11) + pow(x - 2, 10) + 1,
267
      poly::SignCondition::GT,
268
4
      dummy(5));
269
270
2
  cac.computeVariableOrdering();
271
272
4
  auto cover = cac.getUnsatCover();
273
2
  EXPECT_TRUE(!cover.empty());
274
4
  auto nodes = cad::collectConstraints(cover);
275
4
  std::vector<Node> ref{dummy(1), dummy(2), dummy(3), dummy(4), dummy(5)};
276
2
  EXPECT_EQ(nodes, ref);
277
2
}
278
279
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_3)
280
{
281
4
  Options opts;
282
4
  Env env(NodeManager::currentNM(), &opts);
283
4
  cad::CDCAC cac(env, {});
284
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
285
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
286
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
287
288
4
  cac.getConstraints().addConstraint(
289
4
      x * x + y * y + z * z - 1, poly::SignCondition::LT, dummy(1));
290
4
  cac.getConstraints().addConstraint(
291
4
      4 * x * x + (2 * y - 3) * (2 * y - 3) + 4 * z * z - 4,
292
      poly::SignCondition::LT,
293
4
      dummy(2));
294
295
2
  cac.computeVariableOrdering();
296
297
4
  auto cover = cac.getUnsatCover();
298
2
  EXPECT_TRUE(cover.empty());
299
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
300
2
}
301
302
22
TEST_F(TestTheoryWhiteArithCAD, test_cdcac_4)
303
{
304
4
  Options opts;
305
4
  Env env(NodeManager::currentNM(), &opts);
306
4
  cad::CDCAC cac(env, {});
307
2
  poly::Variable x = cac.getConstraints().varMapper()(make_real_variable("x"));
308
2
  poly::Variable y = cac.getConstraints().varMapper()(make_real_variable("y"));
309
2
  poly::Variable z = cac.getConstraints().varMapper()(make_real_variable("z"));
310
311
4
  cac.getConstraints().addConstraint(
312
4
      -z * z + y * y + x * x - 25, poly::SignCondition::GT, dummy(1));
313
4
  cac.getConstraints().addConstraint(
314
4
      (y - x - 6) * z * z - 9 * y * y + x * x - 1,
315
      poly::SignCondition::GT,
316
4
      dummy(2));
317
4
  cac.getConstraints().addConstraint(
318
4
      y * y - 100, poly::SignCondition::LT, dummy(3));
319
320
2
  cac.computeVariableOrdering();
321
322
4
  auto cover = cac.getUnsatCover();
323
2
  EXPECT_TRUE(cover.empty());
324
2
  std::cout << "SAT: " << cac.getModel() << std::endl;
325
2
}
326
327
4
void test_delta(const std::vector<Node>& a)
328
{
329
8
  Options opts;
330
8
  Env env(NodeManager::currentNM(), &opts);
331
8
  cad::CDCAC cac(env, {});
332
4
  cac.reset();
333
28
  for (const Node& n : a)
334
  {
335
24
    cac.getConstraints().addConstraint(n);
336
  }
337
4
  cac.computeVariableOrdering();
338
339
  // Do full theory check here
340
341
8
  auto covering = cac.getUnsatCover();
342
4
  if (covering.empty())
343
  {
344
4
    std::cout << "SAT: " << cac.getModel() << std::endl;
345
  }
346
  else
347
  {
348
    auto mis = cad::collectConstraints(covering);
349
    std::cout << "Collected MIS: " << mis << std::endl;
350
    Assert(!mis.empty()) << "Infeasible subset can not be empty";
351
    Node lem = NodeManager::currentNM()->mkAnd(mis).negate();
352
    std::cout << "UNSAT with MIS: " << lem << std::endl;
353
  }
354
4
}
355
356
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_one)
357
{
358
4
  std::vector<Node> a;
359
4
  Node zero = d_nodeManager->mkConst(Rational(0));
360
4
  Node one = d_nodeManager->mkConst(Rational(1));
361
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
362
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
363
4
  Node g = make_real_variable("g");
364
4
  Node l = make_real_variable("l");
365
4
  Node q = make_real_variable("q");
366
4
  Node s = make_real_variable("s");
367
4
  Node u = make_real_variable("u");
368
369
2
  a.emplace_back(l == mone);
370
2
  a.emplace_back(!(g * s == zero));
371
2
  a.emplace_back(q * s == zero);
372
2
  a.emplace_back(u == zero);
373
2
  a.emplace_back(q == (one + (fifth * g * s)));
374
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
375
376
2
  test_delta(a);
377
2
}
378
379
22
TEST_F(TestTheoryWhiteArithCAD, test_delta_two)
380
{
381
4
  std::vector<Node> a;
382
4
  Node zero = d_nodeManager->mkConst(Rational(0));
383
4
  Node one = d_nodeManager->mkConst(Rational(1));
384
4
  Node mone = d_nodeManager->mkConst(Rational(-1));
385
4
  Node fifth = d_nodeManager->mkConst(Rational(1, 2));
386
4
  Node g = make_real_variable("g");
387
4
  Node l = make_real_variable("l");
388
4
  Node q = make_real_variable("q");
389
4
  Node s = make_real_variable("s");
390
4
  Node u = make_real_variable("u");
391
392
2
  a.emplace_back(l == mone);
393
2
  a.emplace_back(!(g * s == zero));
394
2
  a.emplace_back(u == zero);
395
2
  a.emplace_back(q * s == zero);
396
2
  a.emplace_back(q == (one + (fifth * g * s)));
397
2
  a.emplace_back(l == u + q * s + (fifth * g * s * s));
398
399
2
  test_delta(a);
400
2
}
401
402
22
TEST_F(TestTheoryWhiteArithCAD, test_ran_conversion)
403
{
404
  RealAlgebraicNumber ran(
405
4
      std::vector<Rational>({-2, 0, 1}), Rational(1, 3), Rational(7, 3));
406
  {
407
4
    Node x = make_real_variable("x");
408
4
    Node n = nl::ran_to_node(ran, x);
409
4
    RealAlgebraicNumber back = nl::node_to_ran(n, x);
410
2
    EXPECT_TRUE(ran == back);
411
  }
412
2
}
413
42
}  // namespace cvc5::test
414
415
#endif