GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/fmf/bounded_integers.cpp Lines: 507 541 93.7 %
Date: 2021-11-07 Branches: 1247 2740 45.5 %

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