GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/nl/nl_model.h Lines: 1 1 100.0 %
Date: 2021-09-29 Branches: 14 34 41.2 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, 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
 * Model object for the non-linear extension class.
14
 */
15
16
#ifndef CVC5__THEORY__ARITH__NL__NL_MODEL_H
17
#define CVC5__THEORY__ARITH__NL__NL_MODEL_H
18
19
#include <map>
20
#include <unordered_map>
21
#include <vector>
22
23
#include "expr/kind.h"
24
#include "expr/node.h"
25
26
namespace cvc5 {
27
28
namespace context {
29
class Context;
30
}
31
32
namespace theory {
33
34
class TheoryModel;
35
36
namespace arith {
37
namespace nl {
38
39
class NlLemma;
40
class NonlinearExtension;
41
42
/** Non-linear model finder
43
 *
44
 * This class is responsible for all queries related to the (candidate) model
45
 * that is being processed by the non-linear arithmetic solver. It further
46
 * implements techniques for finding modifications to the current candidate
47
 * model in the case it can determine that a model exists. These include
48
 * techniques based on solving (quadratic) equations and bound analysis.
49
 */
50
1260
class NlModel
51
{
52
  friend class NonlinearExtension;
53
54
 public:
55
  NlModel();
56
  ~NlModel();
57
  /**
58
   * This method is called once at the beginning of a last call effort check,
59
   * where m is the model of the theory of arithmetic. This method resets the
60
   * cache of computed model values.
61
   */
62
  void reset(TheoryModel* m, std::map<Node, Node>& arithModel);
63
  /**
64
   * This method is called when the non-linear arithmetic solver restarts
65
   * its computation of lemmas and models during a last call effort check.
66
   */
67
  void resetCheck();
68
  /**
69
   * This computes model values for terms based on two semantics, a "concrete"
70
   * semantics and an "abstract" semantics.
71
   *
72
   * if isConcrete is true, this means compute the value of n based on its
73
   *          children recursively. (we call this its "concrete" value)
74
   * if isConcrete is false, this means lookup the value of n in the model.
75
   *          (we call this its "abstract" value)
76
   * In other words, !isConcrete treats multiplication terms and transcendental
77
   * function applications as variables, whereas isConcrete computes their
78
   * actual values based on the semantics of multiplication. This is a key
79
   * distinction used in the model-based refinement scheme in Cimatti et al.
80
   * TACAS 2017.
81
   *
82
   * For example, if M( a ) = 2, M( b ) = 3, M( a*b ) = 5, i.e. the variable
83
   * for a*b has been assigned a value 5 by the linear solver, then :
84
   *
85
   *   computeModelValue( a*b, true ) =
86
   *   computeModelValue( a, true )*computeModelValue( b, true ) = 2*3 = 6
87
   * whereas:
88
   *   computeModelValue( a*b, false ) = 5
89
   */
90
  Node computeConcreteModelValue(Node n);
91
  Node computeAbstractModelValue(Node n);
92
  Node computeModelValue(Node n, bool isConcrete);
93
94
  /**
95
   * Compare arithmetic terms i and j based an ordering.
96
   *
97
   * This returns:
98
   *  -1 if i < j, 1 if i > j, or 0 if i == j
99
   *
100
   * If isConcrete is true, we consider the concrete model values of i and j,
101
   * otherwise, we consider their abstract model values. For definitions of
102
   * concrete vs abstract model values, see NlModel::computeModelValue.
103
   *
104
   * If isAbsolute is true, we compare the absolute value of thee above
105
   * values.
106
   */
107
  int compare(Node i, Node j, bool isConcrete, bool isAbsolute);
108
  /**
109
   * Compare arithmetic terms i and j based an ordering.
110
   *
111
   * This returns:
112
   *  -1 if i < j, 1 if i > j, or 0 if i == j
113
   *
114
   * If isAbsolute is true, we compare the absolute value of i and j
115
   */
116
  int compareValue(Node i, Node j, bool isAbsolute) const;
117
118
  //------------------------------ recording model substitutions and bounds
119
  /**
120
   * Adds the model substitution v -> s. This applies the substitution
121
   * { v -> s } to each term in d_check_model_subs and adds v,s to
122
   * d_check_model_vars and d_check_model_subs respectively.
123
   * If this method returns false, then the substitution v -> s is inconsistent
124
   * with the current substitution and bounds.
125
   */
126
  bool addCheckModelSubstitution(TNode v, TNode s);
127
  /**
128
   * Adds the bound x -> < l, u > to the map above, and records the
129
   * approximation ( x, l <= x <= u ) in the model. This method returns false
130
   * if the bound is inconsistent with the current model substitution or
131
   * bounds.
132
   */
133
  bool addCheckModelBound(TNode v, TNode l, TNode u);
134
  /**
135
   * Adds a model witness v -> w to the underlying theory model.
136
   * The witness should only contain a single variable v and evaluate to true
137
   * for exactly one value of v. The variable v is then (implicitly,
138
   * declaratively) assigned to this single value that satisfies the witness w.
139
   */
140
  bool addCheckModelWitness(TNode v, TNode w);
141
  /**
142
   * Have we assigned v in the current checkModel(...) call?
143
   *
144
   * This method returns true if variable v is in the domain of
145
   * d_check_model_bounds or if it occurs in d_check_model_vars.
146
   */
147
  bool hasCheckModelAssignment(Node v) const;
148
  /**
149
   * Checks the current model based on solving for equalities, and using error
150
   * bounds on the Taylor approximation.
151
   *
152
   * If this function returns true, then all assertions in the input argument
153
   * "assertions" are satisfied for all interpretations of variables within
154
   * their computed bounds (as stored in d_check_model_bounds).
155
   *
156
   * For details, see Section 3 of Cimatti et al CADE 2017 under the heading
157
   * "Detecting Satisfiable Formulas".
158
   *
159
   * d is a degree indicating how precise our computations are.
160
   */
161
  bool checkModel(const std::vector<Node>& assertions,
162
                  unsigned d,
163
                  std::vector<NlLemma>& lemmas);
164
  /**
165
   * Set that we have used an approximation during this check. This flag is
166
   * reset on a call to resetCheck. It is set when we use reasoning that
167
   * is limited by a degree of precision we are using. In other words, if we
168
   * used an approximation, then we maybe could still establish a lemma or
169
   * determine the input is SAT if we increased our precision.
170
   */
171
  void setUsedApproximate();
172
  /** Did we use an approximation during this check? */
173
  bool usedApproximate() const;
174
  //------------------------------ end recording model substitutions and bounds
175
176
  /**
177
   * This prints both the abstract and concrete model values for arithmetic
178
   * term n on Trace c with precision prec.
179
   */
180
  void printModelValue(const char* c, Node n, unsigned prec = 5) const;
181
  /**
182
   * This gets mappings that indicate how to repair the model generated by the
183
   * linear arithmetic solver. This method should be called after a successful
184
   * call to checkModel above.
185
   *
186
   * The mapping arithModel is updated by this method to map arithmetic terms v
187
   * to their (exact) value that was computed during checkModel; the mapping
188
   * approximations is updated to store approximate values in the form of a
189
   * pair (P, w), where P is a predicate that describes the possible values of
190
   * v and w is a witness point that satisfies this predicate; the mapping
191
   * witnesses is filled with witness terms that are satisfied by a single
192
   * value.
193
   */
194
  void getModelValueRepair(
195
      std::map<Node, Node>& arithModel,
196
      std::map<Node, std::pair<Node, Node>>& approximations,
197
      std::map<Node, Node>& witnesses,
198
      bool witnessToValue);
199
200
 private:
201
  /** The current model */
202
  TheoryModel* d_model;
203
  /** Get the model value of n from the model object above */
204
  Node getValueInternal(Node n);
205
  /** Does the equality engine of the model have term n? */
206
  bool hasTerm(Node n) const;
207
  /** Get the representative of n in the model */
208
  Node getRepresentative(Node n) const;
209
210
  //---------------------------check model
211
  /**
212
   * This method is used during checkModel(...). It takes as input an
213
   * equality eq. If it returns true, then eq is correct-by-construction based
214
   * on the information stored in our model representation (see
215
   * d_check_model_vars, d_check_model_subs, d_check_model_bounds), and eq
216
   * is added to d_check_model_solved. The equality eq may involve any
217
   * number of variables, and monomials of arbitrary degree. If this method
218
   * returns false, then we did not show that the equality was true in the
219
   * model. This method uses incomplete techniques based on interval
220
   * analysis and quadratic equation solving.
221
   *
222
   * If it can be shown that the equality must be false in the current
223
   * model, then we may add a lemma to lemmas explaining why this is the case.
224
   * For instance, if eq reduces to a univariate quadratic equation with no
225
   * root, we send a conflict clause of the form a*x^2 + b*x + c != 0.
226
   */
227
  bool solveEqualitySimple(Node eq, unsigned d, std::vector<NlLemma>& lemmas);
228
229
  /**
230
   * simple check model for transcendental functions for literal
231
   *
232
   * This method returns true if literal is true for all interpretations of
233
   * transcendental functions within their error bounds (as stored
234
   * in d_check_model_bounds). This is determined by a simple under/over
235
   * approximation of the value of sum of (linear) monomials. For example,
236
   * if we determine that .8 < sin( 1 ) < .9, this function will return
237
   * true for literals like:
238
   *   2.0*sin( 1 ) > 1.5
239
   *   -1.0*sin( 1 ) < -0.79
240
   *   -1.0*sin( 1 ) > -0.91
241
   *   sin( 1 )*sin( 1 ) + sin( 1 ) > 0.0
242
   * It will return false for literals like:
243
   *   sin( 1 ) > 0.85
244
   * It will also return false for literals like:
245
   *   -0.3*sin( 1 )*sin( 2 ) + sin( 2 ) > .7
246
   *   sin( sin( 1 ) ) > .5
247
   * since the bounds on these terms cannot quickly be determined.
248
   */
249
  bool simpleCheckModelLit(Node lit);
250
  bool simpleCheckModelMsum(const std::map<Node, Node>& msum, bool pol);
251
  //---------------------------end check model
252
  /**
253
   * This approximates the square root of positive constant c. If this method
254
   * returns true, then l and u are updated to constants such that
255
   *   l <= sqrt( c ) <= u
256
   * The argument iter is the number of iterations in the binary search to
257
   * perform. By default, this is set to 15, which is usually enough to be
258
   * precise in the majority of simple cases, whereas not prohibitively
259
   * expensive to compute.
260
   */
261
  bool getApproximateSqrt(Node c, Node& l, Node& u, unsigned iter = 15) const;
262
263
  /** commonly used terms */
264
  Node d_zero;
265
  Node d_one;
266
  Node d_two;
267
  Node d_true;
268
  Node d_false;
269
  Node d_null;
270
  /**
271
   * The values that the arithmetic theory solver assigned in the model. This
272
   * corresponds to the set of equalities that linear solver (via TheoryArith)
273
   * is currently sending to TheoryModel during collectModelValues, plus
274
   * additional entries x -> 0 for variables that were unassigned by the linear
275
   * solver.
276
   */
277
  std::map<Node, Node> d_arithVal;
278
  /**
279
   * cache of model values
280
   *
281
   * Stores the the concrete/abstract model values. This is a cache of the
282
   * computeModelValue method.
283
   */
284
  std::map<Node, Node> d_mv[2];
285
  /**
286
   * A substitution from variables that appear in assertions to a solved form
287
   * term. These vectors are ordered in the form:
288
   *   x_1 -> t_1 ... x_n -> t_n
289
   * where x_i is not in the free variables of t_j for j>=i.
290
   */
291
  std::vector<Node> d_check_model_vars;
292
  std::vector<Node> d_check_model_subs;
293
  /**
294
   * lower and upper bounds for check model
295
   *
296
   * For each term t in the domain of this map, if this stores the pair
297
   * (c_l, c_u) then the model M is such that c_l <= M( t ) <= c_u.
298
   *
299
   * We add terms whose value is approximated in the model to this map, which
300
   * includes:
301
   * (1) applications of transcendental functions, whose value is approximated
302
   * by the Taylor series,
303
   * (2) variables we have solved quadratic equations for, whose value
304
   * involves approximations of square roots.
305
   */
306
  std::map<Node, std::pair<Node, Node>> d_check_model_bounds;
307
  /**
308
   * witnesses for check model
309
   *
310
   * Stores witnesses for vatiables that define implicit variable assignments.
311
   * For some variable v, we map to a formulas that is true for exactly one
312
   * value of v.
313
   */
314
  std::map<Node, Node> d_check_model_witnesses;
315
  /**
316
   * The map from literals that our model construction solved, to the variable
317
   * that was solved for. Examples of such literals are:
318
   * (1) Equalities x = t, which we turned into a model substitution x -> t,
319
   * where x not in FV( t ), and
320
   * (2) Equalities a*x*x + b*x + c = 0, which we turned into a model bound
321
   * -b+s*sqrt(b*b-4*a*c)/2a - E <= x <= -b+s*sqrt(b*b-4*a*c)/2a + E.
322
   *
323
   * These literals are exempt from check-model, since they are satisfied by
324
   * definition of our model construction.
325
   */
326
  std::unordered_map<Node, Node> d_check_model_solved;
327
  /** did we use an approximation on this call to last-call effort? */
328
  bool d_used_approx;
329
}; /* class NlModel */
330
331
}  // namespace nl
332
}  // namespace arith
333
}  // namespace theory
334
}  // namespace cvc5
335
336
#endif /* CVC5__THEORY__ARITH__NONLINEAR_EXTENSION_H */