GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/dio_solver.cpp Lines: 423 482 87.8 %
Date: 2021-11-07 Branches: 845 2376 35.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Mathias Preiner, Morgan Deters
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
 * Diophantine equation solver
14
 *
15
 * A Diophantine equation solver for the theory of arithmetic.
16
 */
17
#include "theory/arith/dio_solver.h"
18
19
#include <iostream>
20
21
#include "base/output.h"
22
#include "expr/skolem_manager.h"
23
#include "options/arith_options.h"
24
#include "smt/env.h"
25
#include "smt/smt_statistics_registry.h"
26
#include "theory/arith/partial_model.h"
27
28
using namespace std;
29
30
namespace cvc5 {
31
namespace theory {
32
namespace arith {
33
34
8668
inline Node makeIntegerVariable(){
35
8668
  NodeManager* nm = NodeManager::currentNM();
36
8668
  SkolemManager* sm = nm->getSkolemManager();
37
  return sm->mkDummySkolem("intvar",
38
17336
                           nm->integerType(),
39
26004
                           "is an integer variable created by the dio solver");
40
}
41
42
15273
DioSolver::DioSolver(Env& env)
43
    : EnvObj(env),
44
      d_lastUsedProofVariable(context(), 0),
45
      d_inputConstraints(context()),
46
      d_nextInputConstraintToEnqueue(context(), 0),
47
      d_trail(context()),
48
      d_subs(context()),
49
      d_currentF(),
50
      d_savedQueue(context()),
51
      d_savedQueueIndex(context(), 0),
52
      d_conflictIndex(context()),
53
      d_maxInputCoefficientLength(context(), 0),
54
      d_usedDecomposeIndex(context(), false),
55
      d_lastPureSubstitution(context(), 0),
56
      d_pureSubstitionIter(context(), 0),
57
15273
      d_decompositionLemmaQueue(context())
58
{
59
15273
}
60
61
15273
DioSolver::Statistics::Statistics()
62
15273
    : d_conflictCalls(smtStatisticsRegistry().registerInt(
63
30546
        "theory::arith::dio::conflictCalls")),
64
      d_cutCalls(
65
30546
          smtStatisticsRegistry().registerInt("theory::arith::dio::cutCalls")),
66
30546
      d_cuts(smtStatisticsRegistry().registerInt("theory::arith::dio::cuts")),
67
      d_conflicts(
68
30546
          smtStatisticsRegistry().registerInt("theory::arith::dio::conflicts")),
69
15273
      d_conflictTimer(smtStatisticsRegistry().registerTimer(
70
30546
          "theory::arith::dio::conflictTimer")),
71
      d_cutTimer(
72
91638
          smtStatisticsRegistry().registerTimer("theory::arith::dio::cutTimer"))
73
{
74
15273
}
75
76
90767
bool DioSolver::queueConditions(TrailIndex t){
77
90767
  Debug("queueConditions") << !inConflict() << std::endl;
78
90767
  Debug("queueConditions") << gcdIsOne(t) << std::endl;
79
90767
  Debug("queueConditions") << !debugAnySubstitionApplies(t) << std::endl;
80
90767
  Debug("queueConditions") << !triviallySat(t) << std::endl;
81
90767
  Debug("queueConditions") << !triviallyUnsat(t) << std::endl;
82
83
  return
84
181534
    !inConflict() &&
85
181534
    gcdIsOne(t) &&
86
181534
    !debugAnySubstitionApplies(t) &&
87
272301
    !triviallySat(t) &&
88
181534
    !triviallyUnsat(t);
89
}
90
91
43719
size_t DioSolver::allocateProofVariable() {
92
43719
  Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
93
43719
  if(d_lastUsedProofVariable == d_proofVariablePool.size()){
94
8320
    Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
95
16640
    Node intVar = makeIntegerVariable();
96
8320
    d_proofVariablePool.push_back(Variable(intVar));
97
  }
98
43719
  size_t res = d_lastUsedProofVariable;
99
43719
  d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
100
43719
  return res;
101
}
102
103
104
Node DioSolver::nextPureSubstitution(){
105
  Assert(hasMorePureSubstitutions());
106
  SubIndex curr = d_pureSubstitionIter;
107
  d_pureSubstitionIter = d_pureSubstitionIter + 1;
108
109
  Assert(d_subs[curr].d_fresh.isNull());
110
  Variable v = d_subs[curr].d_eliminated;
111
112
  SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
113
  Polynomial p = sp.getPolynomial();
114
  Constant c = -sp.getConstant();
115
  Polynomial cancelV = p + Polynomial::mkPolynomial(v);
116
  Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
117
  return eq;
118
}
119
120
121
46288
bool DioSolver::debugEqualityInInputEquations(Node eq){
122
  typedef context::CDList<InputConstraint>::const_iterator const_iterator;
123
46288
  const_iterator i=d_inputConstraints.begin(), end = d_inputConstraints.end();
124
2493356
  for(; i != end; ++i){
125
2447068
    Node reason_i = (*i).d_reason;
126
1223534
    if(eq == reason_i){
127
      return true;
128
    }
129
  }
130
46288
  return false;
131
}
132
133
134
46288
void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
135
46288
  Assert(!debugEqualityInInputEquations(reason));
136
46288
  Assert(eq.debugIsIntegral());
137
46288
  Assert(eq.getNode().getKind() == kind::EQUAL);
138
139
90007
  SumPair sp = eq.toSumPair();
140
46288
  if(sp.isNonlinear()){
141
2569
    return;
142
  }
143
144
145
146
43719
  uint32_t length = sp.maxLength();
147
43719
  if(length > d_maxInputCoefficientLength){
148
4078
    d_maxInputCoefficientLength = length;
149
  }
150
151
43719
  size_t varIndex = allocateProofVariable();
152
87438
  Variable proofVariable(d_proofVariablePool[varIndex]);
153
  //Variable proofVariable(makeIntegerVariable());
154
155
43719
  TrailIndex posInTrail = d_trail.size();
156
87438
  Debug("dio::pushInputConstraint") << "pushInputConstraint @ " << posInTrail
157
43719
                                    << " " << eq.getNode()
158
43719
                                    << " " << reason << endl;
159
43719
  d_trail.push_back(Constraint(sp,Polynomial::mkPolynomial(proofVariable)));
160
161
43719
  size_t posInConstraintList = d_inputConstraints.size();
162
43719
  d_inputConstraints.push_back(InputConstraint(reason, posInTrail));
163
164
43719
  d_varToInputConstraintMap[proofVariable.getNode()] = posInConstraintList;
165
}
166
167
168
34169
DioSolver::TrailIndex DioSolver::scaleEqAtIndex(DioSolver::TrailIndex i, const Integer& g){
169
34169
  Assert(g != 0);
170
68338
  Constant invg = Constant::mkConstant(Rational(Integer(1),g));
171
34169
  const SumPair& sp = d_trail[i].d_eq;
172
34169
  const Polynomial& proof = d_trail[i].d_proof;
173
174
68338
  SumPair newSP = sp * invg;
175
68338
  Polynomial newProof = proof * invg;
176
177
34169
  Assert(newSP.isIntegral());
178
34169
  Assert(newSP.gcd() == 1);
179
180
34169
  TrailIndex j = d_trail.size();
181
182
34169
  d_trail.push_back(Constraint(newSP, newProof));
183
184
34169
  Debug("arith::dio") << "scaleEqAtIndex(" << i <<","<<g<<")"<<endl;
185
68338
  Debug("arith::dio") << "derived "<< newSP.getNode()
186
34169
                      <<" with proof " << newProof.getNode() << endl;
187
68338
  return j;
188
}
189
190
124
Node DioSolver::proveIndex(TrailIndex i){
191
124
  Assert(inRange(i));
192
124
  const Polynomial& proof = d_trail[i].d_proof;
193
124
  Assert(!proof.isConstant());
194
195
248
  NodeBuilder nb(kind::AND);
196
517
  for(Polynomial::iterator iter = proof.begin(), end = proof.end(); iter!= end; ++iter){
197
786
    Monomial m = (*iter);
198
393
    Assert(!m.isConstant());
199
786
    VarList vl = m.getVarList();
200
393
    Assert(vl.singleton());
201
786
    Variable v = vl.getHead();
202
203
786
    Node input = proofVariableToReason(v);
204
393
    if(input.getKind() == kind::AND){
205
72
      for(Node::iterator input_iter = input.begin(), input_end = input.end(); input_iter != input_end; ++input_iter){
206
96
	Node inputChild = *input_iter;
207
48
	nb << inputChild;
208
      }
209
    }else{
210
369
      nb << input;
211
    }
212
  }
213
214
124
  Node result = (nb.getNumChildren() == 1) ? nb[0] : (Node)nb;
215
248
  Debug("arith::dio") << "Proof at " << i << " is "
216
124
                      << d_trail[i].d_eq.getNode() << endl
217
124
                      << d_trail[i].d_proof.getNode() << endl
218
124
                      << " which becomes " << result << endl;
219
248
  return result;
220
}
221
222
63716
bool DioSolver::anyCoefficientExceedsMaximum(TrailIndex j) const{
223
63716
  uint32_t length = d_trail[j].d_eq.maxLength();
224
63716
  uint32_t nmonos = d_trail[j].d_eq.getPolynomial().numMonomials();
225
226
  bool result =
227
99726
    nmonos >= 2 &&
228
99726
    length > d_maxInputCoefficientLength + MAX_GROWTH_RATE;
229
63716
  if(Debug.isOn("arith::dio::max") && result){
230
231
    const SumPair& eq = d_trail[j].d_eq;
232
    const Polynomial& proof = d_trail[j].d_proof;
233
234
    Debug("arith::dio::max") << "about to drop:" << std::endl;
235
    Debug("arith::dio::max") << "d_trail[" << j << "].d_eq = " << eq.getNode() << std::endl;
236
    Debug("arith::dio::max") << "d_trail[" << j << "].d_proof = " << proof.getNode() << std::endl;
237
  }
238
63716
  return result;
239
}
240
241
5129
void DioSolver::enqueueInputConstraints(){
242
5129
  Assert(d_currentF.empty());
243
5129
  while(d_savedQueueIndex < d_savedQueue.size()){
244
    d_currentF.push_back(d_savedQueue[d_savedQueueIndex]);
245
    d_savedQueueIndex = d_savedQueueIndex + 1;
246
  }
247
248
86905
  while(d_nextInputConstraintToEnqueue < d_inputConstraints.size()  && !inConflict()){
249
40888
    size_t curr = d_nextInputConstraintToEnqueue;
250
40888
    d_nextInputConstraintToEnqueue = d_nextInputConstraintToEnqueue + 1;
251
252
40888
    TrailIndex i = d_inputConstraints[curr].d_trailPos;
253
40888
    TrailIndex j = applyAllSubstitutionsToIndex(i);
254
255
40888
    if(!triviallySat(j)){
256
40507
      if(triviallyUnsat(j)){
257
        raiseConflict(j);
258
      }else{
259
40507
        TrailIndex k = reduceByGCD(j);
260
261
40507
        if(!inConflict()){
262
39762
          if(triviallyUnsat(k)){
263
            raiseConflict(k);
264
39762
          }else if(!(triviallySat(k) || anyCoefficientExceedsMaximum(k))){
265
39734
            pushToQueueBack(k);
266
          }
267
        }
268
      }
269
    }
270
  }
271
5129
}
272
273
274
/*TODO Currently linear in the size of the Queue
275
 *It is not clear if am O(log n) strategy would be better.
276
 *Right before this in the algorithm is a substitution which could potentially
277
 *effect the key values of everything in the queue.
278
 */
279
26931
void DioSolver::moveMinimumByAbsToQueueFront(){
280
26931
  Assert(!queueEmpty());
281
282
  //Select the minimum element.
283
26931
  size_t indexInQueue = 0;
284
53862
  Monomial minMonomial = d_trail[d_currentF[indexInQueue]].d_minimalMonomial;
285
286
26931
  size_t N = d_currentF.size();
287
722641
  for(size_t i=1; i < N; ++i){
288
1391420
    Monomial curr = d_trail[d_currentF[i]].d_minimalMonomial;
289
695710
    if(curr.absCmp(minMonomial) < 0){
290
99
      indexInQueue = i;
291
99
      minMonomial = curr;
292
    }
293
  }
294
295
26931
  TrailIndex tmp = d_currentF[indexInQueue];
296
26931
  d_currentF[indexInQueue] = d_currentF.front();
297
26931
  d_currentF.front() = tmp;
298
26931
}
299
300
58991
bool DioSolver::queueEmpty() const{
301
58991
  return d_currentF.empty();
302
}
303
304
394
Node DioSolver::columnGcdIsOne() const{
305
788
  std::unordered_map<Node, Integer> gcdMap;
306
307
394
  std::deque<TrailIndex>::const_iterator iter, end;
308
830
  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
309
482
    TrailIndex curr = *iter;
310
918
    Polynomial p = d_trail[curr].d_eq.getPolynomial();
311
918
    Polynomial::iterator monoIter = p.begin(), monoEnd = p.end();
312
2660
    for(; monoIter != monoEnd; ++monoIter){
313
2224
      Monomial m = *monoIter;
314
2224
      VarList vl = m.getVarList();
315
2224
      Node vlNode = vl.getNode();
316
317
2224
      Constant c = m.getConstant();
318
2224
      Integer zc = c.getValue().getNumerator();
319
1135
      if(gcdMap.find(vlNode) == gcdMap.end()){
320
        // have not seen vl yet.
321
        // gcd is c
322
988
        Assert(c.isIntegral());
323
988
        Assert(!m.absCoefficientIsOne());
324
988
        gcdMap.insert(make_pair(vlNode, zc.abs()));
325
      }else{
326
147
        const Integer& currentGcd = gcdMap[vlNode];
327
248
        Integer newGcd = currentGcd.gcd(zc);
328
147
        if(newGcd == 1){
329
46
          return vlNode;
330
        }else{
331
101
          gcdMap[vlNode] = newGcd;
332
        }
333
      }
334
    }
335
  }
336
348
  return Node::null();
337
}
338
339
void DioSolver::saveQueue(){
340
  std::deque<TrailIndex>::const_iterator iter, end;
341
  for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
342
    d_savedQueue.push_back(*iter);
343
  }
344
}
345
346
394
DioSolver::TrailIndex DioSolver::impliedGcdOfOne(){
347
788
  Node canReduce = columnGcdIsOne();
348
394
  if(canReduce.isNull()){
349
348
    return 0;
350
  }else{
351
92
    VarList vl = VarList::parseVarList(canReduce);
352
353
    TrailIndex current;
354
92
    Integer currentCoeff, currentGcd;
355
356
    //step 1 find the first equation containing vl
357
    //Set current and currentCoefficient
358
46
    std::deque<TrailIndex>::const_iterator iter, end;
359
46
    for(iter = d_currentF.begin(), end = d_currentF.end(); true; ++iter){
360
46
      Assert(iter != end);
361
46
      current = *iter;
362
46
      Constant coeff = d_trail[current].d_eq.getPolynomial().getCoefficient(vl);
363
46
      if(!coeff.isZero()){
364
46
        currentCoeff = coeff.getValue().getNumerator();
365
46
        currentGcd = currentCoeff.abs();
366
367
46
        ++iter;
368
46
        break;
369
      }
370
    }
371
372
    //For the rest of the equations keep reducing until the coefficient is one
373
50
    for(; iter != end; ++iter){
374
48
      Debug("arith::dio") << "next round : " << currentCoeff << " " << currentGcd << endl;
375
48
      TrailIndex inQueue = *iter;
376
50
      Constant iqc = d_trail[inQueue].d_eq.getPolynomial().getCoefficient(vl);
377
48
      if(!iqc.isZero()){
378
50
        Integer inQueueCoeff = iqc.getValue().getNumerator();
379
380
        //mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, mpz_t a, mpz_t b);
381
50
        Integer g, s, t;
382
        // g = a*s + b*t
383
48
        Integer::extendedGcd(g, s, t, currentCoeff, inQueueCoeff);
384
385
48
        Debug("arith::dio") << "extendedReduction : " << endl;
386
48
        Debug("arith::dio") << g << " = " << s <<"*"<< currentCoeff << " + " << t <<"*"<< inQueueCoeff << endl;
387
388
48
        Assert(g <= currentGcd);
389
48
        if(g < currentGcd){
390
46
          if(s.sgn() == 0){
391
            Debug("arith::dio") << "extendedReduction drop" << endl;
392
            Assert(inQueueCoeff.divides(currentGcd));
393
            current = *iter;
394
            currentCoeff = inQueueCoeff;
395
            currentGcd = inQueueCoeff.abs();
396
          }else{
397
398
46
            Debug("arith::dio") << "extendedReduction combine" << endl;
399
46
            TrailIndex next = combineEqAtIndexes(current, s, inQueue, t);
400
401
46
            Assert(d_trail[next]
402
                       .d_eq.getPolynomial()
403
                       .getCoefficient(vl)
404
                       .getValue()
405
                       .getNumerator()
406
                   == g);
407
408
46
            current = next;
409
46
            currentCoeff = g;
410
46
            currentGcd = g;
411
46
            if(currentGcd == 1){
412
46
              return current;
413
            }
414
          }
415
        }
416
      }
417
    }
418
    //This is not reachble as it is assured that the gcd of the column is 1
419
    Unreachable();
420
  }
421
}
422
423
5129
bool DioSolver::processEquations(bool allowDecomposition){
424
5129
  Assert(!inConflict());
425
426
5129
  enqueueInputConstraints();
427
58991
  while(! queueEmpty() && !inConflict()){
428
26931
    moveMinimumByAbsToQueueFront();
429
430
26931
    TrailIndex minimum = d_currentF.front();
431
    TrailIndex reduceIndex;
432
433
26931
    Assert(inRange(minimum));
434
26931
    Assert(!inConflict());
435
436
26931
    Debug("arith::dio") << "processEquations " << minimum << " : " << d_trail[minimum].d_eq.getNode() << endl;
437
438
26931
    Assert(queueConditions(minimum));
439
440
26931
    bool canDirectlySolve = d_trail[minimum].d_minimalMonomial.absCoefficientIsOne();
441
442
26931
    std::pair<SubIndex, TrailIndex> p;
443
26931
    if(canDirectlySolve){
444
26537
      d_currentF.pop_front();
445
26537
      p = solveIndex(minimum);
446
26537
      reduceIndex = minimum;
447
    }else{
448
394
      TrailIndex implied = impliedGcdOfOne();
449
450
394
      if(implied != 0){
451
46
        p = solveIndex(implied);
452
46
        reduceIndex = implied;
453
348
      }else if(allowDecomposition){
454
348
        d_currentF.pop_front();
455
348
        p = decomposeIndex(minimum);
456
348
        reduceIndex = minimum;
457
      }else {
458
        // Cannot make progress without decomposeIndex
459
        saveQueue();
460
        break;
461
      }
462
    }
463
464
26931
    SubIndex subIndex = p.first;
465
26931
    TrailIndex next = p.second;
466
26931
    subAndReduceCurrentFByIndex(subIndex);
467
468
26931
    if(next != reduceIndex){
469
348
      if(triviallyUnsat(next)){
470
        raiseConflict(next);
471
348
      }else if(! triviallySat(next) ){
472
348
        pushToQueueBack(next);
473
      }
474
    }
475
  }
476
477
5129
  d_currentF.clear();
478
5129
  return inConflict();
479
}
480
481
3002
Node DioSolver::processEquationsForConflict(){
482
6004
  TimerStat::CodeTimer codeTimer(d_statistics.d_conflictTimer);
483
3002
  ++(d_statistics.d_conflictCalls);
484
485
3002
  Assert(!inConflict());
486
3002
  if(processEquations(true)){
487
124
    ++(d_statistics.d_conflicts);
488
124
    return proveIndex(getConflictIndex());
489
  }else{
490
2878
    return Node::null();
491
  }
492
}
493
494
2127
SumPair DioSolver::processEquationsForCut(){
495
4254
  TimerStat::CodeTimer codeTimer(d_statistics.d_cutTimer);
496
2127
  ++(d_statistics.d_cutCalls);
497
498
2127
  Assert(!inConflict());
499
2127
  if(processEquations(true)){
500
1581
    ++(d_statistics.d_cuts);
501
1581
    return purifyIndex(getConflictIndex());
502
  }else{
503
546
    return SumPair::mkZero();
504
  }
505
}
506
507
508
1581
SumPair DioSolver::purifyIndex(TrailIndex i){
509
  // TODO: "This uses the substitution trail to reverse the substitutions from the sum term. Using the proof term should be more efficient."
510
511
1581
  SumPair curr = d_trail[i].d_eq;
512
513
3162
  Constant negOne = Constant::mkConstant(-1);
514
515
23788
  for(uint32_t revIter = d_subs.size(); revIter > 0; --revIter){
516
22207
    uint32_t i2 = revIter - 1;
517
22429
    Node freshNode = d_subs[i2].d_fresh;
518
22207
    if(freshNode.isNull()){
519
21985
      continue;
520
    }else{
521
444
      Variable var(freshNode);
522
444
      Polynomial vsum = curr.getPolynomial();
523
524
444
      Constant a = vsum.getCoefficient(VarList(var));
525
222
      if(!a.isZero()){
526
193
        const SumPair& sj = d_trail[d_subs[i2].d_constraint].d_eq;
527
193
        Assert(sj.getPolynomial().getCoefficient(VarList(var)).isOne());
528
386
        SumPair newSi = (curr * negOne) + (sj * a);
529
193
        Assert(newSi.getPolynomial().getCoefficient(VarList(var)).isZero());
530
193
        curr = newSi;
531
      }
532
    }
533
  }
534
3162
  return curr;
535
}
536
537
40533
DioSolver::TrailIndex DioSolver::combineEqAtIndexes(DioSolver::TrailIndex i, const Integer& q, DioSolver::TrailIndex j, const Integer& r){
538
81066
  Constant cq = Constant::mkConstant(q);
539
81066
  Constant cr = Constant::mkConstant(r);
540
541
40533
  const SumPair& si = d_trail[i].d_eq;
542
40533
  const SumPair& sj = d_trail[j].d_eq;
543
544
40533
  Debug("arith::dio") << "combineEqAtIndexes(" << i <<","<<q<<","<<j<<","<<r<<")"<<endl;
545
81066
  Debug("arith::dio") << "d_facts[i] = " << si.getNode() << endl
546
40533
                      << "d_facts[j] = " << sj.getNode() << endl;
547
548
549
81066
  SumPair newSi = (si * cq) + (sj * cr);
550
551
552
40533
  const Polynomial& pi = d_trail[i].d_proof;
553
40533
  const Polynomial& pj = d_trail[j].d_proof;
554
81066
  Polynomial newPi = (pi * cq) + (pj * cr);
555
556
40533
  TrailIndex k = d_trail.size();
557
40533
  d_trail.push_back(Constraint(newSi, newPi));
558
559
560
81066
  Debug("arith::dio") << "derived "<< newSi.getNode()
561
40533
                      <<" with proof " << newPi.getNode() << endl;
562
563
81066
  return k;
564
565
}
566
567
void DioSolver::printQueue(){
568
  Debug("arith::dio") << "DioSolver::printQueue()" << endl;
569
  for(TrailIndex i = 0, last = d_trail.size(); i < last; ++i){
570
    Debug("arith::dio") << "d_trail[i].d_eq = " << d_trail[i].d_eq.getNode() << endl;
571
    Debug("arith::dio") << "d_trail[i].d_proof = " << d_trail[i].d_proof.getNode() << endl;
572
  }
573
574
  Debug("arith::dio") << "DioSolver::printSubs()" << endl;
575
  for(SubIndex si=0, sN=d_subs.size(); si < sN; ++si){
576
    Debug("arith::dio") << "d_subs[i] = {"
577
                        << "d_fresh="<< d_subs[si].d_fresh <<","
578
                        << "d_eliminated="<< d_subs[si].d_eliminated.getNode() <<","
579
                        << "d_constraint="<< d_subs[si].d_constraint <<"}" << endl;
580
    Debug("arith::dio") << "d_trail[d_subs[i].d_constraint].d_eq="
581
                        << d_trail[d_subs[si].d_constraint].d_eq.getNode() << endl;
582
  }
583
}
584
585
40888
DioSolver::TrailIndex DioSolver::applyAllSubstitutionsToIndex(DioSolver::TrailIndex trailIndex){
586
40888
  TrailIndex currentIndex = trailIndex;
587
280354
  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
588
239466
    currentIndex = applySubstitution(subIter, currentIndex);
589
  }
590
40888
  return currentIndex;
591
}
592
593
3901924
bool DioSolver::debugSubstitutionApplies(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
594
7803848
  Variable var = d_subs[si].d_eliminated;
595
596
3901924
  const SumPair& curr = d_trail[ti].d_eq;
597
7803848
  Polynomial vsum = curr.getPolynomial();
598
599
7803848
  Constant a = vsum.getCoefficient(VarList(var));
600
7803848
  return !a.isZero();
601
}
602
603
181534
bool DioSolver::debugAnySubstitionApplies(DioSolver::TrailIndex i){
604
4083458
  for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
605
3901924
    if(debugSubstitutionApplies(subIter, i)){
606
      return true;
607
    }
608
  }
609
181534
  return false;
610
}
611
612
26583
std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioSolver::TrailIndex i){
613
26583
  const SumPair& si = d_trail[i].d_eq;
614
615
26583
  Debug("arith::dio") << "before solveIndex("<<i<<":"<<si.getNode()<< ")" << endl;
616
617
#ifdef CVC5_ASSERTIONS
618
53166
  const Polynomial& p = si.getPolynomial();
619
#endif
620
621
26583
  Assert(p.isIntegral());
622
623
26583
  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
624
53166
  const Monomial av = d_trail[i].d_minimalMonomial;
625
626
53166
  VarList vl = av.getVarList();
627
26583
  Assert(vl.singleton());
628
53166
  Variable var = vl.getHead();
629
53166
  Constant a = av.getConstant();
630
53166
  Integer a_abs = a.getValue().getNumerator().abs();
631
632
26583
  Assert(a_abs == 1);
633
634
26583
  TrailIndex ci = !a.isNegative() ? scaleEqAtIndex(i, Integer(-1)) : i;
635
636
26583
  SubIndex subBy = d_subs.size();
637
26583
  d_subs.push_back(Substitution(Node::null(), var, ci));
638
639
26583
  Debug("arith::dio") << "after solveIndex " <<  d_trail[ci].d_eq.getNode() << " for " << av.getNode() << endl;
640
26583
  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
641
         == Constant::mkConstant(-1));
642
643
53166
  return make_pair(subBy, i);
644
}
645
646
348
std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(DioSolver::TrailIndex i){
647
348
  const SumPair& si = d_trail[i].d_eq;
648
649
348
  d_usedDecomposeIndex = true;
650
651
348
  Debug("arith::dio") << "before decomposeIndex("<<i<<":"<<si.getNode()<< ")" << endl;
652
653
#ifdef CVC5_ASSERTIONS
654
696
  const Polynomial& p = si.getPolynomial();
655
#endif
656
657
348
  Assert(p.isIntegral());
658
659
348
  Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
660
348
  const Monomial& av = d_trail[i].d_minimalMonomial;
661
662
696
  VarList vl = av.getVarList();
663
348
  Assert(vl.singleton());
664
696
  Variable var = vl.getHead();
665
696
  Constant a = av.getConstant();
666
696
  Integer a_abs = a.getValue().getNumerator().abs();
667
668
348
  Assert(a_abs > 1);
669
670
  //It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
671
  //We need to handle both cases seperately to ensure termination.
672
696
  Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
673
674
348
  Assert(qr.getKind() == kind::PLUS);
675
348
  Assert(qr.getNumChildren() == 2);
676
696
  SumPair q = SumPair::parseSumPair(qr[0]);
677
696
  SumPair r = SumPair::parseSumPair(qr[1]);
678
679
348
  Assert(q.getPolynomial().getCoefficient(vl) == Constant::mkConstant(1));
680
681
348
  Assert(!r.isZero());
682
696
  Node freshNode = makeIntegerVariable();
683
696
  Variable fresh(freshNode);
684
696
  SumPair fresh_one=SumPair::mkSumPair(fresh);
685
696
  SumPair fresh_a = fresh_one * a;
686
687
696
  SumPair newSI = SumPair(fresh_one) - q;
688
  // this normalizes the coefficient of var to -1
689
690
691
348
  TrailIndex ci = d_trail.size();
692
348
  d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
693
  // no longer reference av safely!
694
348
  addTrailElementAsLemma(ci);
695
696
696
  Debug("arith::dio") << "Decompose ci(" << ci <<":" <<  d_trail[ci].d_eq.getNode()
697
348
                      << ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
698
348
  Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
699
         == Constant::mkConstant(-1));
700
701
696
  SumPair newFact = r + fresh_a;
702
703
348
  TrailIndex nextIndex = d_trail.size();
704
348
  d_trail.push_back(Constraint(newFact, d_trail[i].d_proof));
705
706
348
  SubIndex subBy = d_subs.size();
707
348
  d_subs.push_back(Substitution(freshNode, var, ci));
708
709
348
  Debug("arith::dio") << "Decompose nextIndex " <<  d_trail[nextIndex].d_eq.getNode() << endl;
710
696
  return make_pair(subBy, nextIndex);
711
}
712
713
714
932227
DioSolver::TrailIndex DioSolver::applySubstitution(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
715
1864454
  Variable var = d_subs[si].d_eliminated;
716
932227
  TrailIndex subIndex = d_subs[si].d_constraint;
717
718
932227
  const SumPair& curr = d_trail[ti].d_eq;
719
1864454
  Polynomial vsum = curr.getPolynomial();
720
721
1864454
  Constant a = vsum.getCoefficient(VarList(var));
722
932227
  Assert(a.isIntegral());
723
932227
  if(!a.isZero()){
724
80974
    Integer one(1);
725
40487
    TrailIndex afterSub = combineEqAtIndexes(ti, one, subIndex, a.getValue().getNumerator());
726
40487
    Assert(d_trail[afterSub]
727
               .d_eq.getPolynomial()
728
               .getCoefficient(VarList(var))
729
               .isZero());
730
40487
    return afterSub;
731
  }else{
732
891740
    return ti;
733
  }
734
}
735
736
737
65421
DioSolver::TrailIndex DioSolver::reduceByGCD(DioSolver::TrailIndex ti){
738
65421
  const SumPair& sp = d_trail[ti].d_eq;
739
130842
  Polynomial vsum = sp.getPolynomial();
740
130842
  Constant c = sp.getConstant();
741
742
65421
  Debug("arith::dio") << "reduceByGCD " << vsum.getNode() << endl;
743
65421
  Assert(!vsum.isConstant());
744
130842
  Integer g = vsum.gcd();
745
65421
  Assert(g >= 1);
746
65421
  Debug("arith::dio") << "gcd("<< vsum.getNode() <<")=" << g << " " << c.getValue() << endl;
747
65421
  if(g.divides(c.getValue().getNumerator())){
748
63716
    if(g > 1){
749
9260
      return scaleEqAtIndex(ti, g);
750
    }else{
751
54456
      return ti;
752
    }
753
  }else{
754
1705
    raiseConflict(ti);
755
1705
    return ti;
756
  }
757
}
758
759
290450
bool DioSolver::triviallySat(TrailIndex i){
760
290450
  const SumPair& eq = d_trail[i].d_eq;
761
290450
  if(eq.isConstant()){
762
3385
    return eq.getConstant().isZero();
763
  }else{
764
287065
    return false;
765
  }
766
}
767
768
290069
bool DioSolver::triviallyUnsat(DioSolver::TrailIndex i){
769
290069
  const SumPair& eq = d_trail[i].d_eq;
770
290069
  if(eq.isConstant()){
771
3004
    return !eq.getConstant().isZero();
772
  }else{
773
287065
    return false;
774
  }
775
}
776
777
778
181534
bool DioSolver::gcdIsOne(DioSolver::TrailIndex i){
779
181534
  const SumPair& eq = d_trail[i].d_eq;
780
181534
  return eq.gcd() == Integer(1);
781
}
782
783
26931
void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
784
26931
  size_t N = d_currentF.size();
785
786
26931
  size_t readIter = 0, writeIter = 0;
787
1412453
  for(; readIter < N && !inConflict(); ++readIter){
788
692761
    TrailIndex curr = d_currentF[readIter];
789
692761
    TrailIndex nextTI = applySubstitution(subIndex, curr);
790
692761
    if(nextTI == curr){
791
664843
      d_currentF[writeIter] = curr;
792
664843
      ++writeIter;
793
    }else{
794
27918
      Assert(nextTI != curr);
795
796
27918
      if(triviallyUnsat(nextTI)){
797
        raiseConflict(nextTI);
798
27918
      }else if(!triviallySat(nextTI)){
799
24914
        TrailIndex nextNextTI = reduceByGCD(nextTI);
800
801
24914
        if(!(inConflict() || anyCoefficientExceedsMaximum(nextNextTI))){
802
23754
          Assert(queueConditions(nextNextTI));
803
23754
          d_currentF[writeIter] = nextNextTI;
804
23754
          ++writeIter;
805
        }
806
      }
807
    }
808
  }
809
26931
  if(!inConflict() && writeIter < N){
810
2113
    d_currentF.resize(writeIter);
811
  }
812
26931
}
813
814
348
void DioSolver::addTrailElementAsLemma(TrailIndex i) {
815
348
  if (options().arith.exportDioDecompositions)
816
  {
817
    d_decompositionLemmaQueue.push(i);
818
  }
819
348
}
820
821
Node DioSolver::trailIndexToEquality(TrailIndex i) const {
822
  const SumPair& sp = d_trail[i].d_eq;
823
  Node zero = mkRationalNode(0);
824
  Node eq = (sp.getNode()).eqNode(zero);
825
  return eq;
826
}
827
828
}  // namespace arith
829
}  // namespace theory
830
31137
}  // namespace cvc5