GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/dio_solver.h Lines: 30 39 76.9 %
Date: 2021-11-07 Branches: 18 112 16.1 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Morgan Deters, Mathias Preiner
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
 * A Diophantine equation solver for the theory of arithmetic.
14
 */
15
16
#include "cvc5_private.h"
17
18
#ifndef CVC5__THEORY__ARITH__DIO_SOLVER_H
19
#define CVC5__THEORY__ARITH__DIO_SOLVER_H
20
21
#include <unordered_map>
22
#include <utility>
23
#include <vector>
24
25
#include "context/cdlist.h"
26
#include "context/cdmaybe.h"
27
#include "context/cdo.h"
28
#include "context/cdqueue.h"
29
#include "smt/env_obj.h"
30
#include "theory/arith/normal_form.h"
31
#include "util/rational.h"
32
#include "util/statistics_stats.h"
33
34
namespace cvc5 {
35
namespace context {
36
class Context;
37
}
38
namespace theory {
39
namespace arith {
40
41
15268
class DioSolver : protected EnvObj
42
{
43
 private:
44
  typedef size_t TrailIndex;
45
  typedef size_t InputConstraintIndex;
46
  typedef size_t SubIndex;
47
48
  std::vector<Variable> d_proofVariablePool;
49
  /** Sat context dependent. */
50
  context::CDO<size_t> d_lastUsedProofVariable;
51
52
  /**
53
   * The set of input constraints is stored in a CDList.
54
   * Each constraint point to an element of the trail.
55
   */
56
131157
  struct InputConstraint {
57
    Node d_reason;
58
    TrailIndex d_trailPos;
59
43719
    InputConstraint(Node reason, TrailIndex pos) : d_reason(reason), d_trailPos(pos) {}
60
  };
61
  context::CDList<InputConstraint> d_inputConstraints;
62
63
  /**
64
   * This is the next input constraint to handle.
65
   */
66
  context::CDO<size_t> d_nextInputConstraintToEnqueue;
67
68
69
  /**
70
   * We maintain a map from the variables associated with proofs to an input constraint.
71
   * These variables can then be used in polynomial manipulations.
72
   */
73
  typedef std::unordered_map<Node, InputConstraintIndex>
74
      NodeToInputConstraintIndexMap;
75
  NodeToInputConstraintIndexMap d_varToInputConstraintMap;
76
77
393
  Node proofVariableToReason(const Variable& v) const{
78
393
    Assert(d_varToInputConstraintMap.find(v.getNode())
79
           != d_varToInputConstraintMap.end());
80
393
    InputConstraintIndex pos = (*(d_varToInputConstraintMap.find(v.getNode()))).second;
81
393
    Assert(pos < d_inputConstraints.size());
82
393
    return d_inputConstraints[pos].d_reason;
83
  }
84
85
  /**
86
   * The main work horse of the algorithm, the trail of constraints.
87
   * Each constraint is a SumPair that implicitly represents an equality against 0.
88
   *   d_trail[i].d_eq = (+ c (+ [(* coeff var)])) representing (+ [(* coeff var)]) = -c
89
   * Each constraint has a proof in terms of a linear combination of the input constraints.
90
   *   d_trail[i].d_proof
91
   *
92
   * Each Constraint also a monomial in d_eq.getPolynomial()
93
   * of minimal absolute value by the coefficients.
94
   * d_trail[i].d_minimalMonomial
95
   *
96
   * See Alberto's paper for how linear proofs are maintained for the abstract
97
   * state machine in rules (7), (8) and (9).
98
   */
99
357351
  struct Constraint {
100
    SumPair d_eq;
101
    Polynomial d_proof;
102
    Monomial d_minimalMonomial;
103
119117
    Constraint(const SumPair& eq, const Polynomial& p) :
104
119117
      d_eq(eq), d_proof(p), d_minimalMonomial(d_eq.getPolynomial().selectAbsMinimum())
105
119117
    {}
106
  };
107
  context::CDList<Constraint> d_trail;
108
109
  // /** Compare by d_minimal. */
110
  // struct TrailMinimalCoefficientOrder {
111
  //   const context::CDList<Constraint>& d_trail;
112
  //   TrailMinimalCoefficientOrder(const context::CDList<Constraint>& trail):
113
  //     d_trail(trail)
114
  //   {}
115
116
  //   bool operator()(TrailIndex i, TrailIndex j){
117
  //     return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial);
118
  //   }
119
  // };
120
121
  /**
122
   * A substitution is stored as a constraint in the trail together with
123
   * the variable to be eliminated, and a fresh variable if one was introduced.
124
   * The variable d_subs[i].d_eliminated is substituted using the implicit equality in
125
   * d_trail[d_subs[i].d_constraint]
126
   *  - d_subs[i].d_eliminated is normalized to have coefficient -1 in
127
   *    d_trail[d_subs[i].d_constraint].
128
   *  - d_subs[i].d_fresh is either Node::null() or it is variable it is normalized
129
   *    to have coefficient 1 in d_trail[d_subs[i].d_constraint].
130
   */
131
80793
  struct Substitution {
132
    Node d_fresh;
133
    Variable d_eliminated;
134
    TrailIndex d_constraint;
135
26931
    Substitution(Node f, const Variable& e, TrailIndex c) :
136
26931
      d_fresh(f), d_eliminated(e), d_constraint(c)
137
26931
    {}
138
  };
139
  context::CDList<Substitution> d_subs;
140
141
  /**
142
   * This is the queue of constraints to be processed in the current context level.
143
   * This is to be empty upon entering solver and cleared upon leaving the solver.
144
   *
145
   * All elements in currentF:
146
   * - are fully substituted according to d_subs.
147
   * - !isConstant().
148
   * - If the element is (+ constant (+ [(* coeff var)] )), then the gcd(coeff) = 1
149
   */
150
  std::deque<TrailIndex> d_currentF;
151
  context::CDList<TrailIndex> d_savedQueue;
152
  context::CDO<size_t> d_savedQueueIndex;
153
  context::CDMaybe<TrailIndex> d_conflictIndex;
154
155
  /**
156
   * Drop derived constraints with a coefficient length larger than
157
   * the maximum input constraints length than 2**MAX_GROWTH_RATE.
158
   */
159
  context::CDO<uint32_t> d_maxInputCoefficientLength;
160
  static const uint32_t MAX_GROWTH_RATE = 3;
161
162
  /** Returns true if the element on the trail should be dropped.*/
163
  bool anyCoefficientExceedsMaximum(TrailIndex j) const;
164
165
  /**
166
   * Is true if decomposeIndex has been used in this context.
167
   */
168
  context::CDO<bool> d_usedDecomposeIndex;
169
170
  context::CDO<SubIndex> d_lastPureSubstitution;
171
  context::CDO<SubIndex> d_pureSubstitionIter;
172
173
  /**
174
   * Decomposition lemma queue.
175
   */
176
  context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
177
178
public:
179
180
  /** Construct a Diophantine equation solver with the given context. */
181
 DioSolver(Env& env);
182
183
 /** Returns true if the substitutions use no new variables. */
184
 bool hasMorePureSubstitutions() const
185
 {
186
   return d_pureSubstitionIter < d_lastPureSubstitution;
187
  }
188
189
  Node nextPureSubstitution();
190
191
  /**
192
   * Adds an equality to the queue of the DioSolver.
193
   * orig is blamed in a conflict.
194
   * orig can either be of the form (= p c) or (and ub lb).
195
   * where ub is either (leq p c) or (not (> p [- c 1])), and
196
   * where lb is either (geq p c) or (not (< p [+ c 1]))
197
   *
198
   * If eq cannot be used, this constraint is dropped.
199
   */
200
  void pushInputConstraint(const Comparison& eq, Node reason);
201
202
  /**
203
   * Processes the queue looking for any conflict.
204
   * If a conflict is found, this returns conflict.
205
   * Otherwise, it returns null.
206
   * The conflict is guarenteed to be over literals given in addEquality.
207
   */
208
  Node processEquationsForConflict();
209
210
  /**
211
   * Processes the queue looking for an integer unsatisfiable cutting plane.
212
   * If such a plane is found this returns an entailed plane using no
213
   * fresh variables.
214
   */
215
  SumPair processEquationsForCut();
216
217
private:
218
  /** Returns true if the TrailIndex refers to a element in the trail. */
219
27055
  bool inRange(TrailIndex i) const{
220
27055
    return i < d_trail.size();
221
  }
222
223
  Node columnGcdIsOne() const;
224
225
226
  /**
227
   * Returns true if the context dependent flag for conflicts
228
   * has been raised.
229
   */
230
1082798
  bool inConflict() const { return d_conflictIndex.isSet(); }
231
232
  /** Raises a conflict at the index ti. */
233
1705
  void raiseConflict(TrailIndex ti){
234
1705
    Assert(!inConflict());
235
1705
    d_conflictIndex.set(ti);
236
1705
  }
237
238
  /** Returns the conflict index. */
239
1705
  TrailIndex getConflictIndex() const{
240
1705
    Assert(inConflict());
241
1705
    return d_conflictIndex.get();
242
  }
243
244
  /**
245
   * Allocates a "unique" proof variable.
246
   * This variable is fresh with respect to the context.
247
   * Returns index of the variable in d_variablePool;
248
   */
249
  size_t allocateProofVariable();
250
251
252
  /** Empties the unproccessed input constraints into the queue. */
253
  void enqueueInputConstraints();
254
255
  /**
256
   * Returns true if an input equality is in the map.
257
   * This is expensive and is only for debug assertions.
258
   */
259
  bool debugEqualityInInputEquations(Node eq);
260
261
  /** Applies the substitution at subIndex to currentF. */
262
  void subAndReduceCurrentFByIndex(SubIndex d_subIndex);
263
264
  /**
265
   * Takes as input a TrailIndex i and an integer that divides d_trail[i].d_eq, and
266
   * returns a TrailIndex j s.t.
267
   *   d_trail[j].d_eq = (1/g) d_trail[i].d_eq
268
   * and
269
   *   d_trail[j].d_proof = (1/g) d_trail[i].d_proof.
270
   *
271
   * g must be non-zero.
272
   *
273
   * This corresponds to an application of Alberto's rule (7).
274
   */
275
  TrailIndex scaleEqAtIndex(TrailIndex i, const Integer& g);
276
277
278
  /**
279
   * Takes as input TrailIndex's i and j and Integer's q and r and a TrailIndex k s.t.
280
   *   d_trail[k].d_eq == d_trail[i].d_eq * q + d_trail[j].d_eq * r
281
   * and
282
   *   d_trail[k].d_proof == d_trail[i].d_proof * q + d_trail[j].d_proof * r
283
   *
284
   * This corresponds to an application of Alberto's rule (8).
285
   */
286
  TrailIndex combineEqAtIndexes(TrailIndex i, const Integer& q, TrailIndex j, const Integer& r);
287
288
  /**
289
   * Decomposes the equation at index ti of trail by the variable
290
   * with the lowest coefficient.
291
   * This corresponds to an application of Alberto's rule (9).
292
   *
293
   * Returns a pair of a SubIndex and a TrailIndex.
294
   * The SubIndex is the index of a newly introduced substition.
295
   */
296
  std::pair<SubIndex, TrailIndex> decomposeIndex(TrailIndex ti);
297
298
  /** Solves the index at ti for the value in minimumMonomial. */
299
  std::pair<SubIndex, TrailIndex> solveIndex(TrailIndex ti);
300
301
  /** Prints the queue for debugging purposes to Debug("arith::dio"). */
302
  void printQueue();
303
304
  /**
305
   * Exhaustively applies all substitutions discovered to an element of the trail.
306
   * Returns a TrailIndex corresponding to the substitutions being applied.
307
   */
308
  TrailIndex applyAllSubstitutionsToIndex(TrailIndex i);
309
310
  /**
311
   * Applies a substitution to an element in the trail.
312
   */
313
  TrailIndex applySubstitution(SubIndex s, TrailIndex i);
314
315
  /**
316
   * Reduces the trail node at i by the gcd of the variables.
317
   * Returns the new trail element.
318
   *
319
   * This raises the conflict flag if unsat is detected.
320
   */
321
  TrailIndex reduceByGCD(TrailIndex i);
322
323
  /**
324
   * Returns true if i'th element in the trail is trivially true.
325
   * (0 = 0)
326
   */
327
  bool triviallySat(TrailIndex t);
328
329
  /**
330
   * Returns true if i'th element in the trail is trivially unsatisfiable.
331
   * (1 = 0)
332
   */
333
  bool triviallyUnsat(TrailIndex t);
334
335
  /** Returns true if the gcd of the i'th element of the trail is 1.*/
336
  bool gcdIsOne(TrailIndex t);
337
338
  bool debugAnySubstitionApplies(TrailIndex t);
339
  bool debugSubstitutionApplies(SubIndex si, TrailIndex ti);
340
341
342
  /** Returns true if the queue of nodes to process is empty. */
343
  bool queueEmpty() const;
344
345
  bool queueConditions(TrailIndex t);
346
347
348
40082
  void pushToQueueBack(TrailIndex t){
349
40082
    Assert(queueConditions(t));
350
40082
    d_currentF.push_back(t);
351
40082
  }
352
353
  void pushToQueueFront(TrailIndex t){
354
    Assert(queueConditions(t));
355
    d_currentF.push_front(t);
356
  }
357
358
  /**
359
   * Moves the minimum Constraint by absolute value of the minimum coefficient to
360
   * the front of the queue.
361
   */
362
  void moveMinimumByAbsToQueueFront();
363
364
  void saveQueue();
365
366
  TrailIndex impliedGcdOfOne();
367
368
369
  /**
370
   * Processing the current set of equations.
371
   *
372
   * decomposeIndex() rule is only applied if allowDecomposition is true.
373
   */
374
  bool processEquations(bool allowDecomposition);
375
376
  /**
377
   * Constructs a proof from any d_trail[i] in terms of input literals.
378
   */
379
  Node proveIndex(TrailIndex i);
380
381
  /**
382
   * Returns the SumPair in d_trail[i].d_eq with all of the fresh variables purified out.
383
   */
384
  SumPair purifyIndex(TrailIndex i);
385
386
public:
387
  bool hasMoreDecompositionLemmas() const{
388
    return !d_decompositionLemmaQueue.empty();
389
  }
390
  Node nextDecompositionLemma() {
391
    Assert(hasMoreDecompositionLemmas());
392
    TrailIndex front = d_decompositionLemmaQueue.front();
393
    d_decompositionLemmaQueue.pop();
394
    return trailIndexToEquality(front);
395
  }
396
private:
397
  Node trailIndexToEquality(TrailIndex i) const;
398
  void addTrailElementAsLemma(TrailIndex i);
399
400
public:
401
402
  /** These fields are designed to be accessible to TheoryArith methods. */
403
  class Statistics {
404
  public:
405
406
    IntStat d_conflictCalls;
407
    IntStat d_cutCalls;
408
409
    IntStat d_cuts;
410
    IntStat d_conflicts;
411
412
    TimerStat d_conflictTimer;
413
    TimerStat d_cutTimer;
414
415
    Statistics();
416
  };
417
418
  Statistics d_statistics;
419
}; /* class DioSolver */
420
421
}  // namespace arith
422
}  // namespace theory
423
}  // namespace cvc5
424
425
#endif /* CVC5__THEORY__ARITH__DIO_SOLVER_H */