GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/nl/cad/proof_generator.cpp Lines: 70 84 83.3 %
Date: 2021-08-06 Branches: 103 278 37.1 %

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
 * Implementation of CAD proof generator.
14
 */
15
16
#include "theory/arith/nl/cad/proof_generator.h"
17
18
#ifdef CVC5_POLY_IMP
19
20
#include "proof/lazy_tree_proof_generator.h"
21
#include "theory/arith/nl/poly_conversion.h"
22
#include "util/indexed_root_predicate.h"
23
24
namespace cvc5 {
25
namespace theory {
26
namespace arith {
27
namespace nl {
28
namespace cad {
29
30
namespace {
31
/**
32
 * Retrieves the root indices of the sign-invariant region of v.
33
 *
34
 * We assume that roots holds a sorted list of roots from one polynomial.
35
 * If v is equal to one of these roots, we return (id,id) where id is the index
36
 * of this root within roots. Otherwise, we return the id of the largest root
37
 * below v and the id of the smallest root above v. To make sure a smaller root
38
 * and a larger root always exist, we implicitly extend the roots by -infty and
39
 * infty.
40
 *
41
 * ATTENTION: if we return id, the corresponding root is:
42
 *   - id = 0: -infty
43
 *   - 0 < id <= roots.size(): roots[id-1]
44
 *   - id = roots.size() + 1: infty
45
 */
46
5
std::pair<std::size_t, std::size_t> getRootIDs(
47
    const std::vector<poly::Value>& roots, const poly::Value& v)
48
{
49
6
  for (std::size_t i = 0; i < roots.size(); ++i)
50
  {
51
5
    if (roots[i] == v)
52
    {
53
4
      return {i + 1, i + 1};
54
    }
55
1
    if (roots[i] > v)
56
    {
57
      return {i, i + 1};
58
    }
59
  }
60
1
  return {roots.size(), roots.size() + 1};
61
}
62
63
/**
64
 * Constructs an IndexedRootExpression:
65
 *   var ~rel~ root_k(poly)
66
 * where root_k(poly) is "the k'th root of the polynomial".
67
 *
68
 * @param var The variable that is bounded
69
 * @param rel The relation for this constraint
70
 * @param zero A node representing Rational(0)
71
 * @param k The index of the root (starting with 1)
72
 * @param poly The polynomial whose root shall be considered
73
 * @param vm A variable mapper from cvc5 to libpoly variables
74
 */
75
5
Node mkIRP(const Node& var,
76
           Kind rel,
77
           const Node& zero,
78
           std::size_t k,
79
           const poly::Polynomial& poly,
80
           VariableMapper& vm)
81
{
82
5
  auto* nm = NodeManager::currentNM();
83
10
  auto op = nm->mkConst<IndexedRootPredicate>(IndexedRootPredicate(k));
84
  return nm->mkNode(Kind::INDEXED_ROOT_PREDICATE,
85
                    op,
86
10
                    nm->mkNode(rel, var, zero),
87
20
                    as_cvc_polynomial(poly, vm));
88
}
89
90
}  // namespace
91
92
588
CADProofGenerator::CADProofGenerator(context::Context* ctx,
93
588
                                     ProofNodeManager* pnm)
94
588
    : d_pnm(pnm), d_proofs(pnm, ctx), d_current(nullptr)
95
{
96
588
  d_false = NodeManager::currentNM()->mkConst<bool>(false);
97
588
  d_zero = NodeManager::currentNM()->mkConst<Rational>(0);
98
588
}
99
100
1
void CADProofGenerator::startNewProof()
101
{
102
1
  d_current = d_proofs.allocateProof();
103
1
}
104
2
void CADProofGenerator::startRecursive() { d_current->openChild(); }
105
2
void CADProofGenerator::endRecursive()
106
{
107
2
  d_current->setCurrent(PfRule::ARITH_NL_CAD_RECURSIVE, {}, {d_false}, d_false);
108
2
  d_current->closeChild();
109
2
}
110
5
void CADProofGenerator::startScope()
111
{
112
5
  d_current->openChild();
113
5
  d_current->getCurrent().d_rule = PfRule::SCOPE;
114
5
}
115
6
void CADProofGenerator::endScope(const std::vector<Node>& args)
116
{
117
6
  d_current->setCurrent(PfRule::SCOPE, {}, args, d_false);
118
6
  d_current->closeChild();
119
6
}
120
121
1
ProofGenerator* CADProofGenerator::getProofGenerator() const
122
{
123
1
  return d_current;
124
}
125
126
4
void CADProofGenerator::addDirect(Node var,
127
                                  VariableMapper& vm,
128
                                  const poly::Polynomial& poly,
129
                                  const poly::Assignment& a,
130
                                  poly::SignCondition& sc,
131
                                  const poly::Interval& interval,
132
                                  Node constraint)
133
{
134
8
  if (is_minus_infinity(get_lower(interval))
135
4
      && is_plus_infinity(get_upper(interval)))
136
  {
137
    // "Full conflict", constraint excludes (-inf,inf)
138
    d_current->openChild();
139
    d_current->setCurrent(
140
        PfRule::ARITH_NL_CAD_DIRECT, {constraint}, {d_false}, d_false);
141
    d_current->closeChild();
142
    return;
143
  }
144
8
  std::vector<Node> res;
145
8
  auto roots = poly::isolate_real_roots(poly, a);
146
4
  if (get_lower(interval) == get_upper(interval))
147
  {
148
    // Excludes a single point only
149
    auto ids = getRootIDs(roots, get_lower(interval));
150
    Assert(ids.first == ids.second);
151
    res.emplace_back(mkIRP(var, Kind::EQUAL, d_zero, ids.first, poly, vm));
152
  }
153
  else
154
  {
155
    // Excludes an open interval
156
4
    if (!is_minus_infinity(get_lower(interval)))
157
    {
158
      // Interval has lower bound that is not -inf
159
2
      auto ids = getRootIDs(roots, get_lower(interval));
160
2
      Assert(ids.first == ids.second);
161
2
      Kind rel = poly::get_lower_open(interval) ? Kind::GT : Kind::GEQ;
162
2
      res.emplace_back(mkIRP(var, rel, d_zero, ids.first, poly, vm));
163
    }
164
4
    if (!is_plus_infinity(get_upper(interval)))
165
    {
166
      // Interval has upper bound that is not inf
167
2
      auto ids = getRootIDs(roots, get_upper(interval));
168
2
      Assert(ids.first == ids.second);
169
2
      Kind rel = poly::get_upper_open(interval) ? Kind::LT : Kind::LEQ;
170
2
      res.emplace_back(mkIRP(var, rel, d_zero, ids.first, poly, vm));
171
    }
172
  }
173
  // Add to proof manager
174
4
  startScope();
175
4
  d_current->openChild();
176
12
  d_current->setCurrent(
177
8
      PfRule::ARITH_NL_CAD_DIRECT, {constraint}, {d_false}, d_false);
178
4
  d_current->closeChild();
179
4
  endScope(res);
180
}
181
182
1
std::vector<Node> CADProofGenerator::constructCell(Node var,
183
                                                   const CACInterval& i,
184
                                                   const poly::Assignment& a,
185
                                                   const poly::Value& s,
186
                                                   VariableMapper& vm)
187
{
188
2
  if (is_minus_infinity(get_lower(i.d_interval))
189
1
      && is_plus_infinity(get_upper(i.d_interval)))
190
  {
191
    // "Full conflict", constraint excludes (-inf,inf)
192
    return {};
193
  }
194
195
2
  std::vector<Node> res;
196
197
  // Just use bounds for all polynomials
198
2
  for (const auto& poly : i.d_mainPolys)
199
  {
200
2
    auto roots = poly::isolate_real_roots(poly, a);
201
1
    auto ids = getRootIDs(roots, s);
202
1
    if (ids.first == ids.second)
203
    {
204
      // Excludes a single point only
205
      res.emplace_back(mkIRP(var, Kind::EQUAL, d_zero, ids.first, poly, vm));
206
    }
207
    else
208
    {
209
      // Excludes an open interval
210
1
      if (ids.first > 0)
211
      {
212
        // Interval has lower bound that is not -inf
213
1
        res.emplace_back(mkIRP(var, Kind::GT, d_zero, ids.first, poly, vm));
214
      }
215
1
      if (ids.second <= roots.size())
216
      {
217
        // Interval has upper bound that is not inf
218
        res.emplace_back(mkIRP(var, Kind::LT, d_zero, ids.first, poly, vm));
219
      }
220
    }
221
  }
222
223
1
  return res;
224
}
225
226
std::ostream& operator<<(std::ostream& os, const CADProofGenerator& proof)
227
{
228
  return os << *proof.d_current;
229
}
230
231
}  // namespace cad
232
}  // namespace nl
233
}  // namespace arith
234
}  // namespace theory
235
29322
}  // namespace cvc5
236
237
#endif