GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/nl/nl_model.h Lines: 1 1 100.0 %
Date: 2021-11-07 Branches: 12 24 50.0 %

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