GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/fmf/bounded_integers.cpp Lines: 495 529 93.6 %
Date: 2021-09-10 Branches: 1229 2706 45.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, 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
 * Bounded integers module
14
 *
15
 * This class manages integer bounds for quantifiers.
16
 */
17
18
#include "theory/quantifiers/fmf/bounded_integers.h"
19
20
#include "expr/dtype_cons.h"
21
#include "expr/node_algorithm.h"
22
#include "expr/skolem_manager.h"
23
#include "options/quantifiers_options.h"
24
#include "theory/arith/arith_msum.h"
25
#include "theory/datatypes/theory_datatypes_utils.h"
26
#include "theory/decision_manager.h"
27
#include "theory/quantifiers/first_order_model.h"
28
#include "theory/quantifiers/fmf/model_engine.h"
29
#include "theory/quantifiers/term_enumeration.h"
30
#include "theory/quantifiers/term_util.h"
31
#include "theory/rewriter.h"
32
33
using namespace cvc5;
34
using namespace std;
35
using namespace cvc5::theory;
36
using namespace cvc5::theory::quantifiers;
37
using namespace cvc5::kind;
38
39
678
BoundedIntegers::IntRangeDecisionHeuristic::IntRangeDecisionHeuristic(
40
    Node r,
41
    context::Context* c,
42
    context::Context* u,
43
    Valuation valuation,
44
678
    bool isProxy)
45
678
    : DecisionStrategyFmf(c, valuation), d_range(r), d_ranges_proxied(u)
46
{
47
678
  if( options::fmfBoundLazy() ){
48
4
    SkolemManager* sm = NodeManager::currentNM()->getSkolemManager();
49
4
    d_proxy_range = isProxy ? r : sm->mkDummySkolem("pbir", r.getType());
50
  }else{
51
674
    d_proxy_range = r;
52
  }
53
678
  if( !isProxy ){
54
580
    Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for " << d_range << std::endl;
55
  }
56
678
}
57
2028
Node BoundedIntegers::IntRangeDecisionHeuristic::mkLiteral(unsigned n)
58
{
59
2028
  NodeManager* nm = NodeManager::currentNM();
60
4056
  Node cn = nm->mkConst(Rational(n == 0 ? 0 : n - 1));
61
4056
  return nm->mkNode(n == 0 ? LT : LEQ, d_proxy_range, cn);
62
}
63
64
1999
Node BoundedIntegers::IntRangeDecisionHeuristic::proxyCurrentRangeLemma()
65
{
66
1999
  if (d_range == d_proxy_range)
67
  {
68
1995
    return Node::null();
69
  }
70
4
  unsigned curr = 0;
71
4
  if (!getAssertedLiteralIndex(curr))
72
  {
73
    return Node::null();
74
  }
75
4
  if (d_ranges_proxied.find(curr) != d_ranges_proxied.end())
76
  {
77
    return Node::null();
78
  }
79
4
  d_ranges_proxied[curr] = true;
80
4
  NodeManager* nm = NodeManager::currentNM();
81
8
  Node currLit = getLiteral(curr);
82
  Node lem =
83
      nm->mkNode(EQUAL,
84
                 currLit,
85
8
                 nm->mkNode(curr == 0 ? LT : LEQ,
86
                            d_range,
87
16
                            nm->mkConst(Rational(curr == 0 ? 0 : curr - 1))));
88
4
  return lem;
89
}
90
91
1211
BoundedIntegers::BoundedIntegers(Env& env,
92
                                 QuantifiersState& qs,
93
                                 QuantifiersInferenceManager& qim,
94
                                 QuantifiersRegistry& qr,
95
1211
                                 TermRegistry& tr)
96
1211
    : QuantifiersModule(env, qs, qim, qr, tr)
97
{
98
1211
}
99
100
2422
BoundedIntegers::~BoundedIntegers() {}
101
102
1360
void BoundedIntegers::presolve() {
103
1360
  d_bnd_it.clear();
104
1360
}
105
106
11550
bool BoundedIntegers::hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ) {
107
11550
  if( visited.find( b )==visited.end() ){
108
10788
    visited[b] = true;
109
10788
    if( b.getKind()==BOUND_VARIABLE ){
110
1167
      if( !isBound( f, b ) ){
111
610
        return true;
112
      }
113
    }else{
114
17041
      for( unsigned i=0; i<b.getNumChildren(); i++ ){
115
8256
        if( hasNonBoundVar( f, b[i], visited ) ){
116
836
          return true;
117
        }
118
      }
119
    }
120
  }
121
10104
  return false;
122
}
123
3294
bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) {
124
6588
  std::map< Node, bool > visited;
125
6588
  return hasNonBoundVar( f, b, visited );
126
}
127
128
751
bool BoundedIntegers::processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ) {
129
751
  if( n.getKind()==EQUAL ){
130
2171
    for( unsigned i=0; i<2; i++ ){
131
2894
      Node t = n[i];
132
1474
      if( !hasNonBoundVar( q, n[1-i] ) ){
133
1054
        if( t==v ){
134
24
          v_cases.push_back( n[1-i] );
135
24
          return true;
136
1030
        }else if( v.isNull() && t.getKind()==BOUND_VARIABLE ){
137
30
          v = t;
138
30
          v_cases.push_back( n[1-i] );
139
30
          return true;
140
        }
141
      }
142
    }
143
  }
144
697
  return false;
145
}
146
147
112
void BoundedIntegers::processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ){
148
112
  if( visited.find( n )==visited.end() ){
149
112
    visited[n] = true;
150
112
    if( n.getKind()==BOUND_VARIABLE && !isBound( q, n ) ){
151
43
      bvs.push_back( n );
152
    //injective operators
153
69
    }else if( n.getKind()==kind::APPLY_CONSTRUCTOR ){
154
60
      for( unsigned i=0; i<n.getNumChildren(); i++ ){
155
36
        processMatchBoundVars( q, n[i], bvs, visited );
156
      }
157
    }
158
  }
159
112
}
160
161
10188
void BoundedIntegers::process( Node q, Node n, bool pol,
162
                               std::map< Node, unsigned >& bound_lit_type_map,
163
                               std::map< int, std::map< Node, Node > >& bound_lit_map,
164
                               std::map< int, std::map< Node, bool > >& bound_lit_pol_map,
165
                               std::map< int, std::map< Node, Node > >& bound_int_range_term,
166
                               std::map< Node, std::vector< Node > >& bound_fixed_set ){
167
10188
  if( n.getKind()==OR || n.getKind()==AND ){
168
1850
    if( (n.getKind()==OR)==pol ){
169
7043
      for( unsigned i=0; i<n.getNumChildren(); i++) {
170
5543
        process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set );
171
      }
172
    }else{
173
      //if we are ( x != t1 ^ ...^ x != tn ), then x can be bound to { t1...tn }
174
700
      Node conj = n;
175
350
      if( !pol ){
176
        conj = TermUtil::simpleNegate( conj );
177
      }
178
350
      Trace("bound-int-debug") << "Process possible finite disequality conjunction : " << conj << std::endl;
179
350
      Assert(conj.getKind() == AND);
180
700
      Node v;
181
700
      std::vector< Node > v_cases;
182
350
      bool success = true;
183
400
      for( unsigned i=0; i<conj.getNumChildren(); i++ ){
184
376
        if( conj[i].getKind()==NOT && processEqDisjunct( q, conj[i][0], v, v_cases ) ){
185
          //continue
186
        }else{
187
326
          Trace("bound-int-debug") << "...failed due to " << conj[i] << std::endl;
188
326
          success = false;
189
326
          break;
190
        }
191
      }
192
350
      if( success && !isBound( q, v ) ){
193
10
        Trace("bound-int-debug") << "Success with variable " << v << std::endl;
194
10
        bound_lit_type_map[v] = BOUND_FIXED_SET;
195
10
        bound_lit_map[3][v] = n;
196
10
        bound_lit_pol_map[3][v] = pol;
197
10
        bound_fixed_set[v].clear();
198
10
        bound_fixed_set[v].insert( bound_fixed_set[v].end(), v_cases.begin(), v_cases.end() );
199
      }
200
    }
201
8338
  }else if( n.getKind()==EQUAL ){
202
1233
    if( !pol ){
203
      // non-applied DER on x != t, x can be bound to { t }
204
1290
      Node v;
205
1290
      std::vector< Node > v_cases;
206
645
      if( processEqDisjunct( q, n, v, v_cases ) ){
207
4
        if( !isBound( q, v ) ){
208
2
          bound_lit_type_map[v] = BOUND_FIXED_SET;
209
2
          bound_lit_map[3][v] = n;
210
2
          bound_lit_pol_map[3][v] = pol;
211
2
          Assert(v_cases.size() == 1);
212
2
          bound_fixed_set[v].clear();
213
2
          bound_fixed_set[v].push_back( v_cases[0] );
214
        }
215
      }
216
    }
217
7105
  }else if( n.getKind()==NOT ){
218
2989
    process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set );
219
4116
  }else if( n.getKind()==GEQ ){
220
3414
    if( n[0].getType().isInteger() ){
221
6828
      std::map< Node, Node > msum;
222
3414
      if (ArithMSum::getMonomialSumLit(n, msum))
223
      {
224
3414
        Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is monomial sum : " << std::endl;
225
3414
        ArithMSum::debugPrintMonomialSum(msum, "bound-int-debug");
226
10564
        for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){
227
7150
          if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){
228
            //if not bound in another way
229
1745
            if( bound_lit_type_map.find( it->first )==bound_lit_type_map.end() || bound_lit_type_map[it->first] == BOUND_INT_RANGE ){
230
3480
              Node veq;
231
1740
              if (ArithMSum::isolate(it->first, msum, veq, GEQ) != 0)
232
              {
233
3480
                Node n1 = veq[0];
234
3480
                Node n2 = veq[1];
235
1740
                if(pol){
236
                  //flip
237
863
                  n1 = veq[1];
238
863
                  n2 = veq[0];
239
863
                  if( n1.getKind()==BOUND_VARIABLE ){
240
2
                    n2 = ArithMSum::offset(n2, 1);
241
                  }else{
242
861
                    n1 = ArithMSum::offset(n1, -1);
243
                  }
244
863
                  veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 );
245
                }
246
1740
                Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl;
247
3480
                Node t = n1==it->first ? n2 : n1;
248
1740
                if( !hasNonBoundVar( q, t ) ) {
249
1554
                  Trace("bound-int-debug") << "The bound is relevant." << std::endl;
250
1554
                  int loru = n1==it->first ? 0 : 1;
251
1554
                  bound_lit_type_map[it->first] = BOUND_INT_RANGE;
252
1554
                  bound_int_range_term[loru][it->first] = t;
253
1554
                  bound_lit_map[loru][it->first] = n;
254
1554
                  bound_lit_pol_map[loru][it->first] = pol;
255
                }else{
256
186
                  Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl;
257
                }
258
              }
259
            }
260
          }
261
        }
262
      }
263
    }
264
702
  }else if( n.getKind()==MEMBER ){
265
80
    if( !pol && !hasNonBoundVar( q, n[1] ) ){
266
152
      std::vector< Node > bound_vars;
267
152
      std::map< Node, bool > visited;
268
76
      processMatchBoundVars( q, n[0], bound_vars, visited );
269
119
      for( unsigned i=0; i<bound_vars.size(); i++ ){
270
86
        Node v = bound_vars[i];
271
43
        Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is membership." << std::endl;
272
43
        bound_lit_type_map[v] = BOUND_SET_MEMBER;
273
43
        bound_lit_map[2][v] = n;
274
43
        bound_lit_pol_map[2][v] = pol;
275
      }
276
    }
277
  }else{
278
622
    Assert(n.getKind() != LEQ && n.getKind() != LT && n.getKind() != GT);
279
  }
280
10188
}
281
282
14581
bool BoundedIntegers::needsCheck( Theory::Effort e ) {
283
14581
  return e==Theory::EFFORT_LAST_CALL;
284
}
285
286
3329
void BoundedIntegers::check(Theory::Effort e, QEffort quant_e)
287
{
288
3329
  if (quant_e != QEFFORT_STANDARD)
289
  {
290
2220
    return;
291
  }
292
1109
  Trace("bint-engine") << "---Bounded Integers---" << std::endl;
293
1109
  bool addedLemma = false;
294
  // make sure proxies are up-to-date with range
295
3108
  for (const Node& r : d_ranges)
296
  {
297
3998
    Node prangeLem = d_rms[r]->proxyCurrentRangeLemma();
298
1999
    if (!prangeLem.isNull())
299
    {
300
8
      Trace("bound-int-lemma")
301
4
          << "*** bound int : proxy lemma : " << prangeLem << std::endl;
302
4
      d_qim.addPendingLemma(prangeLem, InferenceId::QUANTIFIERS_BINT_PROXY);
303
4
      addedLemma = true;
304
    }
305
  }
306
1109
  Trace("bint-engine") << "   addedLemma = " << addedLemma << std::endl;
307
}
308
904
void BoundedIntegers::setBoundedVar(Node q, Node v, BoundVarType bound_type)
309
{
310
904
  d_bound_type[q][v] = bound_type;
311
904
  d_set_nums[q][v] = d_set[q].size();
312
904
  d_set[q].push_back( v );
313
1808
  Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v
314
904
                         << std::endl;
315
904
}
316
317
881
void BoundedIntegers::checkOwnership(Node f)
318
{
319
  //this needs to be done at preregister since it affects e.g. QuantDSplit's preregister
320
881
  Trace("bound-int") << "check ownership quantifier " << f << std::endl;
321
322
  // determine if we should look at the quantified formula at all
323
881
  if (!options::fmfBound())
324
  {
325
    // only applying it to internal quantifiers
326
684
    QuantAttributes& qattr = d_qreg.getQuantAttributes();
327
684
    if (!qattr.isInternal(f))
328
    {
329
113
      Trace("bound-int") << "...not internal, skip" << std::endl;
330
113
      return;
331
    }
332
  }
333
334
768
  NodeManager* nm = NodeManager::currentNM();
335
768
  SkolemManager* sm = nm->getSkolemManager();
336
337
  bool success;
338
1656
  do{
339
3312
    std::map< Node, unsigned > bound_lit_type_map;
340
3312
    std::map< int, std::map< Node, Node > > bound_lit_map;
341
3312
    std::map< int, std::map< Node, bool > > bound_lit_pol_map;
342
3312
    std::map< int, std::map< Node, Node > > bound_int_range_term;
343
3312
    std::map< Node, std::vector< Node > > bound_fixed_set;
344
1656
    success = false;
345
1656
    process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set );
346
    //for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){
347
2516
    for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){
348
1720
      Node v = it->first;
349
860
      if( !isBound( f, v ) ){
350
860
        bool setBoundVar = false;
351
860
        if( it->second==BOUND_INT_RANGE ){
352
          //must have both
353
813
          std::map<Node, Node>& blm0 = bound_lit_map[0];
354
813
          std::map<Node, Node>& blm1 = bound_lit_map[1];
355
813
          if (blm0.find(v) != blm0.end() && blm1.find(v) != blm1.end())
356
          {
357
715
            setBoundedVar( f, v, BOUND_INT_RANGE );
358
715
            setBoundVar = true;
359
2145
            for( unsigned b=0; b<2; b++ ){
360
              //set the bounds
361
1430
              Assert(bound_int_range_term[b].find(v)
362
                     != bound_int_range_term[b].end());
363
1430
              d_bounds[b][f][v] = bound_int_range_term[b][v];
364
            }
365
1430
            Node r = nm->mkNode(MINUS, d_bounds[1][f][v], d_bounds[0][f][v]);
366
715
            d_range[f][v] = Rewriter::rewrite(r);
367
715
            Trace("bound-int") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl;
368
          }
369
47
        }else if( it->second==BOUND_SET_MEMBER ){
370
          // only handles infinite element types currently (cardinality is not
371
          // supported for finite element types #1123). Regardless, this is
372
          // typically not a limitation since this variable can be bound in a
373
          // standard way below since its type is finite.
374
35
          if (!d_qstate.isFiniteType(v.getType()))
375
          {
376
35
            setBoundedVar(f, v, BOUND_SET_MEMBER);
377
35
            setBoundVar = true;
378
35
            d_setm_range[f][v] = bound_lit_map[2][v][1];
379
35
            d_setm_range_lit[f][v] = bound_lit_map[2][v];
380
35
            d_range[f][v] = nm->mkNode(CARD, d_setm_range[f][v]);
381
70
            Trace("bound-int") << "Variable " << v
382
35
                               << " is bound because of set membership literal "
383
35
                               << bound_lit_map[2][v] << std::endl;
384
          }
385
12
        }else if( it->second==BOUND_FIXED_SET ){
386
12
          setBoundedVar(f, v, BOUND_FIXED_SET);
387
12
          setBoundVar = true;
388
34
          for (unsigned i = 0; i < bound_fixed_set[v].size(); i++)
389
          {
390
44
            Node t = bound_fixed_set[v][i];
391
22
            if (expr::hasBoundVar(t))
392
            {
393
6
              d_fixed_set_ngr_range[f][v].push_back(t);
394
            }
395
            else
396
            {
397
16
              d_fixed_set_gr_range[f][v].push_back(t);
398
            }
399
          }
400
24
          Trace("bound-int") << "Variable " << v
401
12
                             << " is bound because of disequality conjunction "
402
12
                             << bound_lit_map[3][v] << std::endl;
403
        }
404
860
        if( setBoundVar ){
405
762
          success = true;
406
          //set Attributes on literals
407
2286
          for( unsigned b=0; b<2; b++ ){
408
1524
            std::map<Node, Node>& blm = bound_lit_map[b];
409
1524
            if (blm.find(v) != blm.end())
410
            {
411
1430
              std::map<Node, bool>& blmp = bound_lit_pol_map[b];
412
              // WARNING_CANDIDATE:
413
              // This assertion may fail. We intentionally do not enable this in
414
              // production as it is considered safe for this to fail. We fail
415
              // the assertion in debug mode to have this instance raised to
416
              // our attention.
417
1430
              Assert(blmp.find(v) != blmp.end());
418
              BoundIntLitAttribute bila;
419
1430
              bound_lit_map[b][v].setAttribute(bila, blmp[v] ? 1 : 0);
420
            }
421
            else
422
            {
423
94
              Assert(it->second != BOUND_INT_RANGE);
424
            }
425
          }
426
        }
427
      }
428
    }
429
1656
    if( !success ){
430
      //resort to setting a finite bound on a variable
431
1858
      for( unsigned i=0; i<f[0].getNumChildren(); i++) {
432
1090
        if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
433
178
          TypeNode tn = f[0][i].getType();
434
370
          if ((tn.isSort() && d_qstate.isFiniteType(tn))
435
436
              || d_qreg.getQuantifiersBoundInference().mayComplete(tn))
436
          {
437
142
            success = true;
438
142
            setBoundedVar( f, f[0][i], BOUND_FINITE );
439
142
            break;
440
          }
441
        }
442
      }
443
    }
444
  }while( success );
445
446
768
  if( Trace.isOn("bound-int") ){
447
    Trace("bound-int") << "Bounds are : " << std::endl;
448
    for( unsigned i=0; i<f[0].getNumChildren(); i++) {
449
      Node v = f[0][i];
450
      if( std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end() ){
451
        Assert(d_bound_type[f].find(v) != d_bound_type[f].end());
452
        if( d_bound_type[f][v]==BOUND_INT_RANGE ){
453
          Trace("bound-int") << "  " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl;
454
        }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){
455
          if( d_setm_range_lit[f][v][0]==v ){
456
            Trace("bound-int") << "  " << v << " in " << d_setm_range[f][v] << std::endl;
457
          }else{
458
            Trace("bound-int") << "  " << v << " unifiable in " << d_setm_range_lit[f][v] << std::endl;
459
          }
460
        }else if( d_bound_type[f][v]==BOUND_FIXED_SET ){
461
          Trace("bound-int") << "  " << v << " in { ";
462
          for (TNode fnr : d_fixed_set_ngr_range[f][v])
463
          {
464
            Trace("bound-int") << fnr << " ";
465
          }
466
          for (TNode fgr : d_fixed_set_gr_range[f][v])
467
          {
468
            Trace("bound-int") << fgr << " ";
469
          }
470
          Trace("bound-int") << "}" << std::endl;
471
        }else if( d_bound_type[f][v]==BOUND_FINITE ){
472
          Trace("bound-int") << "  " << v << " has small finite type." << std::endl;
473
        }else{
474
          Trace("bound-int") << "  " << v << " has unknown bound." << std::endl;
475
          Assert(false);
476
        }
477
      }else{
478
        Trace("bound-int") << "  " << "*** " << v << " is unbounded." << std::endl;
479
      }
480
    }
481
  }
482
483
768
  bool bound_success = true;
484
1670
  for( unsigned i=0; i<f[0].getNumChildren(); i++) {
485
914
    if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
486
12
      Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl;
487
12
      bound_success = false;
488
12
      break;
489
    }
490
  }
491
492
768
  if( bound_success ){
493
756
    d_bound_quants.push_back( f );
494
756
    DecisionManager* dm = d_qim.getDecisionManager();
495
1658
    for( unsigned i=0; i<d_set[f].size(); i++) {
496
1804
      Node v = d_set[f][i];
497
902
      std::map< Node, Node >::iterator itr = d_range[f].find( v );
498
902
      if( itr != d_range[f].end() ){
499
1496
        Node r = itr->second;
500
748
        Assert(!r.isNull());
501
748
        bool isProxy = false;
502
748
        if (expr::hasBoundVar(r))
503
        {
504
          // introduce a new bound
505
          Node new_range =
506
196
              sm->mkDummySkolem("bir", r.getType(), "bound for term");
507
98
          d_nground_range[f][v] = r;
508
98
          d_range[f][v] = new_range;
509
98
          r = new_range;
510
98
          isProxy = true;
511
        }
512
748
        if( !r.isConst() ){
513
708
          if (d_rms.find(r) == d_rms.end())
514
          {
515
678
            Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl;
516
678
            d_ranges.push_back( r );
517
2034
            d_rms[r].reset(
518
                new IntRangeDecisionHeuristic(r,
519
678
                                              d_qstate.getSatContext(),
520
678
                                              d_qstate.getUserContext(),
521
678
                                              d_qstate.getValuation(),
522
678
                                              isProxy));
523
678
            dm->registerStrategy(DecisionManager::STRAT_QUANT_BOUND_INT_SIZE,
524
678
                                 d_rms[r].get());
525
          }
526
        }
527
      }
528
    }
529
  }
530
}
531
532
6691
bool BoundedIntegers::isBound(Node q, Node v) const
533
{
534
6691
  std::map<Node, std::vector<Node> >::const_iterator its = d_set.find(q);
535
6691
  if (its == d_set.end())
536
  {
537
2823
    return false;
538
  }
539
7736
  return std::find(its->second.begin(), its->second.end(), v)
540
11604
         != its->second.end();
541
}
542
543
6941
BoundVarType BoundedIntegers::getBoundVarType(Node q, Node v) const
544
{
545
  std::map<Node, std::map<Node, BoundVarType> >::const_iterator itb =
546
6941
      d_bound_type.find(q);
547
6941
  if (itb == d_bound_type.end())
548
  {
549
    return BOUND_NONE;
550
  }
551
6941
  std::map<Node, BoundVarType>::const_iterator it = itb->second.find(v);
552
6941
  if (it == itb->second.end())
553
  {
554
14
    return BOUND_NONE;
555
  }
556
6927
  return it->second;
557
}
558
559
4475
void BoundedIntegers::getBoundVarIndices(Node q,
560
                                         std::vector<unsigned>& indices) const
561
{
562
4475
  std::map<Node, std::vector<Node> >::const_iterator it = d_set.find(q);
563
4475
  if (it != d_set.end())
564
  {
565
10233
    for (const Node& v : it->second)
566
    {
567
5816
      indices.push_back(TermUtil::getVariableNum(q, v));
568
    }
569
  }
570
4475
}
571
572
4743
void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) {
573
4743
  l = d_bounds[0][f][v];
574
4743
  u = d_bounds[1][f][v];
575
4743
  if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){
576
    //get the substitution
577
2790
    std::vector< Node > vars;
578
2790
    std::vector< Node > subs;
579
1395
    if( getRsiSubsitution( f, v, vars, subs, rsi ) ){
580
1320
      u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
581
1320
      l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
582
    }else{
583
75
      u = Node::null();
584
75
      l = Node::null();
585
    }
586
  }
587
4743
}
588
589
2409
void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) {
590
2409
  getBounds( f, v, rsi, l, u );
591
2409
  Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl;
592
2409
  if( !l.isNull() ){
593
2334
    l = d_treg.getModel()->getValue(l);
594
  }
595
2409
  if( !u.isNull() ){
596
2334
    u = d_treg.getModel()->getValue(u);
597
  }
598
2409
  Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl;
599
2409
  return;
600
}
601
602
794
bool BoundedIntegers::isGroundRange(Node q, Node v)
603
{
604
794
  if (isBound(q, v))
605
  {
606
794
    if (d_bound_type[q][v] == BOUND_INT_RANGE)
607
    {
608
1950
      return !expr::hasBoundVar(getLowerBound(q, v))
609
1950
             && !expr::hasBoundVar(getUpperBound(q, v));
610
    }
611
144
    else if (d_bound_type[q][v] == BOUND_SET_MEMBER)
612
    {
613
12
      return !expr::hasBoundVar(d_setm_range[q][v]);
614
    }
615
132
    else if (d_bound_type[q][v] == BOUND_FIXED_SET)
616
    {
617
132
      return !d_fixed_set_ngr_range[q][v].empty();
618
    }
619
  }
620
  return false;
621
}
622
623
70
Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) {
624
70
  Node sr = d_setm_range[q][v];
625
70
  if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){
626
16
    Trace("bound-int-rsi-debug")
627
8
        << sr << " is non-ground, apply substitution..." << std::endl;
628
    //get the substitution
629
16
    std::vector< Node > vars;
630
16
    std::vector< Node > subs;
631
8
    if( getRsiSubsitution( q, v, vars, subs, rsi ) ){
632
8
      Trace("bound-int-rsi-debug")
633
4
          << "  apply " << vars << " -> " << subs << std::endl;
634
4
      sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
635
    }else{
636
4
      sr = Node::null();
637
    }
638
  }
639
70
  return sr;
640
}
641
642
70
Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) {
643
140
  Node sr = getSetRange( q, v, rsi );
644
70
  if (sr.isNull())
645
  {
646
4
    return sr;
647
  }
648
66
  Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl;
649
66
  Assert(!expr::hasFreeVar(sr));
650
132
  Node sro = sr;
651
66
  sr = d_treg.getModel()->getValue(sr);
652
  // if non-constant, then sr does not occur in the model, we fail
653
66
  if (!sr.isConst())
654
  {
655
    return Node::null();
656
  }
657
66
  Trace("bound-int-rsi") << "Value is " << sr << std::endl;
658
66
  if (sr.getKind() == EMPTYSET)
659
  {
660
2
    return sr;
661
  }
662
64
  NodeManager* nm = NodeManager::currentNM();
663
128
  Node nsr;
664
128
  TypeNode tne = sr.getType().getSetElementType();
665
666
  // we can use choice functions for canonical symbolic instantiations
667
64
  unsigned srCard = 0;
668
128
  while (sr.getKind() == UNION)
669
  {
670
32
    srCard++;
671
32
    sr = sr[0];
672
  }
673
64
  Assert(sr.getKind() == SINGLETON);
674
64
  srCard++;
675
  // choices[i] stores the canonical symbolic representation of the (i+1)^th
676
  // element of sro
677
128
  std::vector<Node> choices;
678
128
  Node srCardN = nm->mkNode(CARD, sro);
679
128
  Node choice_i;
680
160
  for (unsigned i = 0; i < srCard; i++)
681
  {
682
96
    if (i == d_setm_choice[sro].size())
683
    {
684
38
      choice_i = nm->mkBoundVar(tne);
685
38
      choices.push_back(choice_i);
686
76
      Node cBody = nm->mkNode(MEMBER, choice_i, sro);
687
38
      if (choices.size() > 1)
688
      {
689
14
        cBody = nm->mkNode(AND, cBody, nm->mkNode(DISTINCT, choices));
690
      }
691
38
      choices.pop_back();
692
76
      Node bvl = nm->mkNode(BOUND_VAR_LIST, choice_i);
693
76
      Node cMinCard = nm->mkNode(LEQ, srCardN, nm->mkConst(Rational(i)));
694
38
      choice_i = nm->mkNode(WITNESS, bvl, nm->mkNode(OR, cMinCard, cBody));
695
38
      d_setm_choice[sro].push_back(choice_i);
696
    }
697
96
    Assert(i < d_setm_choice[sro].size());
698
96
    choice_i = d_setm_choice[sro][i];
699
96
    choices.push_back(choice_i);
700
192
    Node sChoiceI = nm->mkSingleton(choice_i.getType(), choice_i);
701
96
    if (nsr.isNull())
702
    {
703
64
      nsr = sChoiceI;
704
    }
705
    else
706
    {
707
32
      nsr = nm->mkNode(UNION, nsr, sChoiceI);
708
    }
709
  }
710
  // turns the concrete model value of sro into a canonical representation
711
  //   e.g.
712
  // singleton(0) union singleton(1)
713
  //   becomes
714
  // C1 union ( witness y. card(S)<=1 OR ( y in S AND distinct( y, C1 ) ) )
715
  // where C1 = ( witness x. card(S)<=0 OR x in S ).
716
64
  Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl;
717
64
  return nsr;
718
}
719
720
1455
bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) {
721
722
1455
  Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl;
723
1455
  Assert(d_set_nums[q].find(v) != d_set_nums[q].end());
724
1455
  int vindex = d_set_nums[q][v];
725
1455
  Assert(d_set_nums[q][v] == vindex);
726
1455
  Trace("bound-int-rsi-debug") << "  index order is " << vindex << std::endl;
727
  //must take substitution for all variables that are iterating at higher level
728
3190
  for( int i=0; i<vindex; i++) {
729
1735
    Assert(d_set_nums[q][d_set[q][i]] == i);
730
1735
    Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl;
731
1735
    int vo = rsi->getVariableOrder(i);
732
1735
    Assert(q[0][vo] == d_set[q][i]);
733
3470
    TypeNode tn = d_set[q][i].getType();
734
    // If the type of tn is not closed enumerable, we must map the value back
735
    // to a term that appears in the same equivalence class as the constant.
736
    // Notice that this is to ensure that unhandled values (e.g. uninterpreted
737
    // constants, datatype values) do not enter instantiations/lemmas, which
738
    // can lead to refutation unsoundness. However, it is important that we
739
    // conversely do *not* map terms to values in other cases. In particular,
740
    // replacing a constant c with a term t can lead to solution unsoundness
741
    // if we are instantiating a quantified formula that corresponds to a
742
    // reduction for t, since then the reduction is using circular reasoning:
743
    // the current value of t is being used to reason about the range of
744
    // its axiomatization. This is limited to reductions in the theory of
745
    // strings, which use quantification on integers only. Note this
746
    // impacts only quantified formulas with 2+ dimensions and dependencies
747
    // between dimensions, e.g. str.indexof_re reduction.
748
3470
    Node t = rsi->getCurrentTerm(vo, !tn.isClosedEnumerable());
749
1735
    Trace("bound-int-rsi") << "term : " << t << std::endl;
750
1735
    vars.push_back( d_set[q][i] );
751
1735
    subs.push_back( t );
752
  }
753
754
  //check if it has been instantiated
755
1455
  if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){
756
87
    if( d_bound_type[q][v]==BOUND_INT_RANGE || d_bound_type[q][v]==BOUND_SET_MEMBER ){
757
      //must add the lemma
758
158
      Node nn = d_nground_range[q][v];
759
79
      nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
760
158
      Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] );
761
79
      Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl;
762
79
      d_qim.lemma(lem, InferenceId::QUANTIFIERS_BINT_MIN_NG);
763
    }
764
87
    return false;
765
  }else{
766
1368
    return true;
767
  }
768
}
769
770
80
Node BoundedIntegers::matchBoundVar( Node v, Node t, Node e ){
771
80
  if( t==v ){
772
32
    return e;
773
48
  }else if( t.getKind()==kind::APPLY_CONSTRUCTOR ){
774
32
    if( e.getKind()==kind::APPLY_CONSTRUCTOR ){
775
      if( t.getOperator() != e.getOperator() ) {
776
        return Node::null();
777
      }
778
    }
779
32
    NodeManager* nm = NodeManager::currentNM();
780
32
    const DType& dt = datatypes::utils::datatypeOf(t.getOperator());
781
32
    unsigned index = datatypes::utils::indexOf(t.getOperator());
782
48
    for( unsigned i=0; i<t.getNumChildren(); i++ ){
783
64
      Node u;
784
48
      if( e.getKind()==kind::APPLY_CONSTRUCTOR ){
785
        u = matchBoundVar( v, t[i], e[i] );
786
      }else{
787
        Node se = nm->mkNode(APPLY_SELECTOR_TOTAL,
788
96
                             dt[index].getSelectorInternal(e.getType(), i),
789
192
                             e);
790
48
        u = matchBoundVar( v, t[i], se );
791
      }
792
48
      if( !u.isNull() ){
793
32
        return u;
794
      }
795
    }
796
  }
797
16
  return Node::null();
798
}
799
800
2839
bool BoundedIntegers::getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ) {
801
2839
  if( initial || !isGroundRange( q, v ) ){
802
2569
    elements.clear();
803
2569
    BoundVarType bvt = getBoundVarType(q, v);
804
2569
    if( bvt==BOUND_INT_RANGE ){
805
4818
      Node l, u;
806
2409
      getBoundValues( q, v, rsi, l, u );
807
2409
      if( l.isNull() || u.isNull() ){
808
75
        Trace("bound-int-warn") << "WARNING: Could not find integer bounds in model for " << v << " in " << q << std::endl;
809
        //failed, abort the iterator
810
75
        return false;
811
      }else{
812
2334
        Trace("bound-int-rsi") << "Can limit bounds of " << v << " to " << l << "..." << u << std::endl;
813
4668
        Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) );
814
4668
        Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) );
815
4668
        Node tl = l;
816
4668
        Node tu = u;
817
2334
        getBounds( q, v, rsi, tl, tu );
818
2334
        Assert(!tl.isNull() && !tu.isNull());
819
2334
        if (ra.isConst() && ra.getConst<bool>())
820
        {
821
2334
          long rr = range.getConst<Rational>().getNumerator().getLong()+1;
822
2334
          Trace("bound-int-rsi")  << "Actual bound range is " << rr << std::endl;
823
15324
          for (long k = 0; k < rr; k++)
824
          {
825
25980
            Node t = NodeManager::currentNM()->mkNode(PLUS, tl, NodeManager::currentNM()->mkConst( Rational(k) ) );
826
12990
            t = Rewriter::rewrite( t );
827
12990
            elements.push_back( t );
828
          }
829
2334
          return true;
830
        }else{
831
          Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << v << "." << std::endl;
832
          return false;
833
        }
834
      }
835
160
    }else if( bvt==BOUND_SET_MEMBER  ){
836
140
      Node srv = getSetRangeValue( q, v, rsi );
837
70
      if( srv.isNull() ){
838
4
        Trace("bound-int-warn") << "WARNING: Could not find set bound in model for " << v << " in " << q << std::endl;
839
4
        return false;
840
      }else{
841
66
        Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl;
842
66
        if( srv.getKind()!=EMPTYSET ){
843
          //collect the elements
844
128
          while( srv.getKind()==UNION ){
845
32
            Assert(srv[1].getKind() == kind::SINGLETON);
846
32
            elements.push_back( srv[1][0] );
847
32
            srv = srv[0];
848
          }
849
64
          Assert(srv.getKind() == kind::SINGLETON);
850
64
          elements.push_back( srv[0] );
851
          //check if we need to do matching, for literals like ( tuple( v ) in S )
852
128
          Node t = d_setm_range_lit[q][v][0];
853
64
          if( t!=v ){
854
32
            std::vector< Node > elements_tmp;
855
16
            elements_tmp.insert( elements_tmp.end(), elements.begin(), elements.end() );
856
16
            elements.clear();
857
48
            for( unsigned i=0; i<elements_tmp.size(); i++ ){
858
              //do matching to determine v -> u
859
64
              Node u = matchBoundVar( v, t, elements_tmp[i] );
860
32
              Trace("bound-int-rsi-debug") << "  unification : " << elements_tmp[i] << " = " << t << " yields " << v << " -> " << u << std::endl;
861
32
              if( !u.isNull() ){
862
32
                elements.push_back( u );
863
              }
864
            }
865
          }
866
        }
867
66
        return true;
868
      }
869
90
    }else if( bvt==BOUND_FIXED_SET ){
870
86
      std::map< Node, std::vector< Node > >::iterator it = d_fixed_set_gr_range[q].find( v );
871
86
      if( it!=d_fixed_set_gr_range[q].end() ){
872
174
        for( unsigned i=0; i<it->second.size(); i++ ){
873
108
          elements.push_back( it->second[i] );
874
        }
875
      }
876
86
      it = d_fixed_set_ngr_range[q].find( v );
877
86
      if( it!=d_fixed_set_ngr_range[q].end() ){
878
104
        std::vector< Node > vars;
879
104
        std::vector< Node > subs;
880
52
        if( getRsiSubsitution( q, v, vars, subs, rsi ) ){
881
98
          for( unsigned i=0; i<it->second.size(); i++ ){
882
108
            Node t = it->second[i].substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
883
54
            elements.push_back( t );
884
          }
885
44
          return true;
886
        }else{
887
8
          return false;
888
        }
889
      }else{
890
34
        return true;
891
      }
892
    }else{
893
4
      return false;
894
    }
895
  }else{
896
    //no change required
897
270
    return true;
898
  }
899
29502
}