1 /********************* */
2 /*! \file inst_strategy_e_matching.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Morgan Deters, Mathias Preiner
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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.\endverbatim
12 ** \brief Implementation of e matching instantiation strategies
15 #include "theory/quantifiers/ematching/inst_strategy_e_matching.h"
17 #include "theory/quantifiers/ematching/pattern_term_selector.h"
18 #include "theory/quantifiers/quant_relevance.h"
19 #include "theory/quantifiers_engine.h"
20 #include "util/random.h"
22 using namespace CVC4::kind
;
23 using namespace CVC4::theory::inst
;
27 namespace quantifiers
{
30 //1 : user patterns (when user-pat!={resort,ignore}), auto-gen patterns (for non-user pattern quantifiers, or when user-pat={resort,ignore})
31 //2 : user patterns (when user-pat=resort), auto gen patterns (for user pattern quantifiers when user-pat=use)
33 // user-pat=interleave alternates between use and resort
35 struct sortQuantifiersForSymbol
{
36 QuantRelevance
* d_quant_rel
;
37 std::map
< Node
, Node
> d_op_map
;
38 bool operator() (Node i
, Node j
) {
39 size_t nqfsi
= d_quant_rel
->getNumQuantifiersForSymbol(d_op_map
[i
]);
40 size_t nqfsj
= d_quant_rel
->getNumQuantifiersForSymbol(d_op_map
[j
]);
43 }else if( nqfsi
>nqfsj
){
51 bool operator() (Node i
, Node j
) {
52 int32_t wi
= TriggerTermInfo::getTriggerWeight(i
);
53 int32_t wj
= TriggerTermInfo::getTriggerWeight(j
);
61 InstStrategyAutoGenTriggers::InstStrategyAutoGenTriggers(QuantifiersEngine
* qe
,
64 : InstStrategy(qe
, qs
), d_quant_rel(qr
)
66 //how to select trigger terms
67 d_tr_strategy
= options::triggerSelMode();
68 //whether to select new triggers during the search
69 if( options::incrementTriggers() ){
70 d_regenerate_frequency
= 3;
73 d_regenerate_frequency
= 1;
78 void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort
){
79 Trace("inst-alg-debug") << "reset auto-gen triggers" << std::endl
;
81 for( unsigned r
=0; r
<2; r
++ ){
82 std::map
<Node
, std::map
<inst::Trigger
*, bool> >& agts
=
83 d_auto_gen_trigger
[r
];
84 for (std::pair
<const Node
, std::map
<inst::Trigger
*, bool> >& agt
: agts
)
86 for (std::map
<inst::Trigger
*, bool>::iterator it
= agt
.second
.begin();
87 it
!= agt
.second
.end();
90 it
->first
->resetInstantiationRound();
91 it
->first
->reset(Node::null());
95 d_processed_trigger
.clear();
96 Trace("inst-alg-debug") << "done reset auto-gen triggers" << std::endl
;
99 InstStrategyStatus
InstStrategyAutoGenTriggers::process(Node f
,
100 Theory::Effort effort
,
103 options::UserPatMode upMode
= d_quantEngine
->getInstUserPatMode();
104 if (hasUserPatterns(f
) && upMode
== options::UserPatMode::TRUST
)
106 return InstStrategyStatus::STATUS_UNKNOWN
;
108 int peffort
= (hasUserPatterns(f
) && upMode
!= options::UserPatMode::IGNORE
109 && upMode
!= options::UserPatMode::RESORT
)
114 return InstStrategyStatus::STATUS_UNFINISHED
;
116 Trace("inst-alg") << "-> Auto-gen instantiate " << f
<< "..." << std::endl
;
120 if (d_counter
.find(f
) == d_counter
.end())
126 gen
= d_regenerate
&& d_counter
[f
] % d_regenerate_frequency
== 0;
136 if (d_counter
[f
] == 0 && d_auto_gen_trigger
[0][f
].empty()
137 && d_auto_gen_trigger
[1][f
].empty() && f
.getNumChildren() == 2)
139 Trace("trigger-warn") << "Could not find trigger for " << f
<< std::endl
;
142 if (options::triggerActiveSelMode() != options::TriggerActiveSelMode::ALL
)
145 Trigger
* max_trigger
= nullptr;
146 std::map
<Trigger
*, bool>& agt
= d_auto_gen_trigger
[0][f
];
147 for (std::map
<Trigger
*, bool>::iterator it
= agt
.begin(); it
!= agt
.end();
150 Trigger
* t
= it
->first
;
151 int score
= t
->getActiveScore();
152 if (options::triggerActiveSelMode() == options::TriggerActiveSelMode::MIN
)
154 if (score
>= 0 && (score
< max_score
|| max_score
< 0))
162 if (score
> max_score
)
170 if (max_trigger
!= nullptr)
172 agt
[max_trigger
] = true;
176 bool hasInst
= false;
177 for (unsigned r
= 0; r
< 2; r
++)
179 std::map
<Trigger
*, bool>& agt
= d_auto_gen_trigger
[r
][f
];
180 for (std::map
<Trigger
*, bool>::iterator it
= agt
.begin(); it
!= agt
.end();
183 Trigger
* tr
= it
->first
;
184 if (tr
== nullptr || !it
->second
)
186 // trigger is null or currently disabled
189 if (d_processed_trigger
[f
].find(tr
) != d_processed_trigger
[f
].end())
191 // trigger is already processed this round
194 d_processed_trigger
[f
][tr
] = true;
195 Trace("process-trigger") << " Process ";
196 tr
->debugPrint("process-trigger");
197 Trace("process-trigger") << "..." << std::endl
;
198 unsigned numInst
= tr
->addInstantiations();
199 hasInst
= numInst
> 0 || hasInst
;
200 Trace("process-trigger")
201 << " Done, numInst = " << numInst
<< "." << std::endl
;
202 d_quantEngine
->d_statistics
.d_instantiations_auto_gen
+= numInst
;
205 d_quantEngine
->d_statistics
.d_multi_trigger_instantiations
+= numInst
;
207 if (d_qstate
.isInConflict())
212 if (d_qstate
.isInConflict() || (hasInst
&& options::multiTriggerPriority()))
217 return InstStrategyStatus::STATUS_UNKNOWN
;
220 void InstStrategyAutoGenTriggers::generateTriggers( Node f
){
221 Trace("auto-gen-trigger-debug") << "Generate triggers for " << f
<< ", #var=" << f
[0].getNumChildren() << "..." << std::endl
;
223 // first, generate the set of pattern terms
224 if (!generatePatternTerms(f
))
226 Trace("auto-gen-trigger-debug")
227 << "...failed to generate pattern terms" << std::endl
;
231 // then, group them to make triggers
232 unsigned rmin
= d_patTerms
[0][f
].empty() ? 1 : 0;
233 unsigned rmax
= options::multiTriggerWhenSingle() ? 1 : rmin
;
234 for (unsigned r
= rmin
; r
<= rmax
; r
++)
236 std::vector
<Node
> patTerms
;
237 std::vector
<Node
>& ptc
= d_patTerms
[r
][f
];
238 for (const Node
& p
: ptc
)
240 if (r
== 1 || d_single_trigger_gen
.find(p
) == d_single_trigger_gen
.end())
242 patTerms
.push_back(p
);
245 if (patTerms
.empty())
249 Trace("auto-gen-trigger") << "Generate trigger for " << f
<< std::endl
;
250 // sort terms based on relevance
251 if (options::relevantTriggers())
254 sortQuantifiersForSymbol sqfs
;
255 sqfs
.d_quant_rel
= d_quant_rel
;
256 for (const Node
& p
: patTerms
)
258 Assert(d_pat_to_mpat
.find(p
) != d_pat_to_mpat
.end());
259 Assert(d_pat_to_mpat
[p
].hasOperator());
260 sqfs
.d_op_map
[p
] = d_pat_to_mpat
[p
].getOperator();
262 // sort based on # occurrences (this will cause Trigger to select rarer
264 std::sort(patTerms
.begin(), patTerms
.end(), sqfs
);
265 if (Debug
.isOn("relevant-trigger"))
267 Debug("relevant-trigger") << "Terms based on relevance: " << std::endl
;
268 for (const Node
& p
: patTerms
)
270 Debug("relevant-trigger")
271 << " " << p
<< " from " << d_pat_to_mpat
[p
] << " (";
272 Debug("relevant-trigger") << d_quant_rel
->getNumQuantifiersForSymbol(
273 d_pat_to_mpat
[p
].getOperator())
278 // now, generate the trigger...
280 if (d_is_single_trigger
[patTerms
[0]])
282 tr
= Trigger::mkTrigger(d_quantEngine
,
286 Trigger::TR_RETURN_NULL
,
287 d_num_trigger_vars
[f
]);
288 d_single_trigger_gen
[patTerms
[0]] = true;
292 // only generate multi trigger if option set, or if no single triggers
294 if (!d_patTerms
[0][f
].empty())
296 if (options::multiTriggerWhenSingle())
298 Trace("multi-trigger-debug")
299 << "Resort to choosing multi-triggers..." << std::endl
;
306 // if we are re-generating triggers, shuffle based on some method
307 if (d_made_multi_trigger
[f
])
309 std::shuffle(patTerms
.begin(),
311 Random::getRandom()); // shuffle randomly
315 d_made_multi_trigger
[f
] = true;
317 // will possibly want to get an old trigger
318 tr
= Trigger::mkTrigger(d_quantEngine
,
323 d_num_trigger_vars
[f
]);
327 // did not generate a trigger
331 if (tr
->isMultiTrigger())
333 // only add a single multi-trigger
336 // if we are generating additional triggers...
338 if (index
< patTerms
.size())
340 // check if similar patterns exist, and if so, add them additionally
341 unsigned nqfs_curr
= 0;
342 if (options::relevantTriggers())
345 d_quant_rel
->getNumQuantifiersForSymbol(patTerms
[0].getOperator());
349 while (success
&& index
< patTerms
.size()
350 && d_is_single_trigger
[patTerms
[index
]])
353 if (!options::relevantTriggers()
354 || d_quant_rel
->getNumQuantifiersForSymbol(
355 patTerms
[index
].getOperator())
358 d_single_trigger_gen
[patTerms
[index
]] = true;
359 Trigger
* tr2
= Trigger::mkTrigger(d_quantEngine
,
363 Trigger::TR_RETURN_NULL
,
364 d_num_trigger_vars
[f
]);
374 bool InstStrategyAutoGenTriggers::generatePatternTerms(Node f
)
376 if (d_patTerms
[0].find(f
) != d_patTerms
[0].end())
381 // determine all possible pattern terms based on trigger term selection
382 // strategy d_tr_strategy
383 d_patTerms
[0][f
].clear();
384 d_patTerms
[1][f
].clear();
385 bool ntrivTriggers
= options::relationalTriggers();
386 std::vector
<Node
> patTermsF
;
387 std::map
<Node
, inst::TriggerTermInfo
> tinfo
;
388 TermUtil
* tu
= d_quantEngine
->getTermUtil();
389 NodeManager
* nm
= NodeManager::currentNM();
390 // well-defined function: can assume LHS is only pattern
391 if (options::quantFunWellDefined())
393 Node hd
= QuantAttributes::getFunDefHead(f
);
396 hd
= tu
->substituteBoundVariablesToInstConstants(hd
, f
);
397 patTermsF
.push_back(hd
);
398 tinfo
[hd
].init(f
, hd
);
401 // otherwise, use algorithm for collecting pattern terms
402 if (patTermsF
.empty())
404 Node bd
= tu
->getInstConstantBody(f
);
405 PatternTermSelector
pts(f
, d_tr_strategy
, d_user_no_gen
[f
], true);
406 pts
.collect(bd
, patTermsF
, tinfo
);
410 std::sort(patTermsF
.begin(), patTermsF
.end(), st
);
412 if (Trace
.isOn("auto-gen-trigger-debug"))
414 Trace("auto-gen-trigger-debug")
415 << "Collected pat terms for " << bd
416 << ", no-patterns : " << d_user_no_gen
[f
].size() << std::endl
;
417 for (const Node
& p
: patTermsF
)
419 Assert(tinfo
.find(p
) != tinfo
.end());
420 Trace("auto-gen-trigger-debug") << " " << p
<< std::endl
;
421 Trace("auto-gen-trigger-debug2")
422 << " info = [" << tinfo
[p
].d_reqPol
<< ", "
423 << tinfo
[p
].d_reqPolEq
<< ", " << tinfo
[p
].d_fv
.size() << "]"
426 Trace("auto-gen-trigger-debug") << std::endl
;
429 // sort into single/multi triggers, calculate which terms should not be
431 std::map
<Node
, bool> vcMap
;
432 std::map
<Node
, bool> rmPatTermsF
;
433 int32_t last_weight
= -1;
434 for (const Node
& p
: patTermsF
)
436 Assert(p
.getKind() != NOT
);
438 inst::TriggerTermInfo
& tip
= tinfo
[p
];
439 for (const Node
& v
: tip
.d_fv
)
441 if (vcMap
.find(v
) == vcMap
.end())
447 int32_t curr_w
= TriggerTermInfo::getTriggerWeight(p
);
448 // triggers whose value is maximum (2) are considered expendable.
449 if (ntrivTriggers
&& !newVar
&& last_weight
!= -1 && curr_w
> last_weight
452 Trace("auto-gen-trigger-debug")
453 << "...exclude expendible non-trivial trigger : " << p
<< std::endl
;
454 rmPatTermsF
[p
] = true;
458 last_weight
= curr_w
;
461 d_num_trigger_vars
[f
] = vcMap
.size();
462 if (d_num_trigger_vars
[f
] > 0
463 && d_num_trigger_vars
[f
] < f
[0].getNumChildren())
465 Trace("auto-gen-trigger-partial")
466 << "Quantified formula : " << f
<< std::endl
;
467 Trace("auto-gen-trigger-partial")
468 << "...does not contain all variables in triggers!!!" << std::endl
;
469 // Invoke partial trigger strategy: partition variables of quantified
470 // formula into (X,Y) where X are contained in a trigger and Y are not.
471 // We then force a split of the quantified formula so that it becomes:
472 // forall X. forall Y. P( X, Y )
473 // and hence is treatable by E-matching. We only do this for "standard"
474 // quantified formulas (those with only two children), since this
475 // technique should not be used for e.g. quantifiers marked for
476 // quantifier elimination.
478 QuantAttributes::computeQuantAttributes(f
, qa
);
479 if (options::partialTriggers() && qa
.isStandard())
481 std::vector
<Node
> vcs
[2];
482 for (size_t i
= 0, nchild
= f
[0].getNumChildren(); i
< nchild
; i
++)
484 Node ic
= tu
->getInstantiationConstant(f
, i
);
485 vcs
[vcMap
.find(ic
) == vcMap
.end() ? 0 : 1].push_back(f
[0][i
]);
487 for (size_t i
= 0; i
< 2; i
++)
489 d_vc_partition
[i
][f
] = nm
->mkNode(BOUND_VAR_LIST
, vcs
[i
]);
497 for (const Node
& patf
: patTermsF
)
500 if (rmPatTermsF
.find(pat
) != rmPatTermsF
.end())
504 Trace("auto-gen-trigger-debug")
505 << "...processing pattern " << pat
<< std::endl
;
507 // process the pattern: if it has a required polarity, consider it
508 Assert(tinfo
.find(pat
) != tinfo
.end());
509 int rpol
= tinfo
[pat
].d_reqPol
;
510 Node rpoleq
= tinfo
[pat
].d_reqPolEq
;
511 size_t num_fv
= tinfo
[pat
].d_fv
.size();
512 Trace("auto-gen-trigger-debug")
513 << "...required polarity for " << pat
<< " is " << rpol
514 << ", eq=" << rpoleq
<< std::endl
;
517 Assert(rpol
== 1 || rpol
== -1);
518 if (TriggerTermInfo::isRelationalTrigger(pat
))
520 pat
= rpol
== -1 ? pat
.negate() : pat
;
524 Assert(TriggerTermInfo::isAtomicTrigger(pat
));
525 if (pat
.getType().isBoolean() && rpoleq
.isNull())
527 if (options::literalMatchMode() == options::LiteralMatchMode::USE
)
529 pat
= pat
.eqNode(nm
->mkConst(rpol
== -1)).negate();
531 else if (options::literalMatchMode()
532 != options::LiteralMatchMode::NONE
)
534 pat
= pat
.eqNode(nm
->mkConst(rpol
== 1));
539 Assert(!rpoleq
.isNull());
542 if (options::literalMatchMode() != options::LiteralMatchMode::NONE
)
544 // all equivalence classes except rpoleq
545 pat
= pat
.eqNode(rpoleq
).negate();
550 if (options::literalMatchMode() == options::LiteralMatchMode::AGG
)
552 // only equivalence class rpoleq
553 pat
= pat
.eqNode(rpoleq
);
558 Trace("auto-gen-trigger-debug") << "...got : " << pat
<< std::endl
;
562 if (TriggerTermInfo::isRelationalTrigger(pat
))
564 // consider both polarities
565 addPatternToPool(f
, pat
.negate(), num_fv
, mpat
);
568 addPatternToPool(f
, pat
, num_fv
, mpat
);
570 // tinfo not used below this point
571 d_made_multi_trigger
[f
] = false;
572 if (Trace
.isOn("auto-gen-trigger"))
574 Trace("auto-gen-trigger")
575 << "Single trigger pool for " << f
<< " : " << std::endl
;
576 std::vector
<Node
>& spats
= d_patTerms
[0][f
];
577 for (size_t i
= 0, psize
= spats
.size(); i
< psize
; i
++)
579 Trace("auto-gen-trigger") << " " << spats
[i
] << std::endl
;
581 std::vector
<Node
>& mpats
= d_patTerms
[0][f
];
584 Trace("auto-gen-trigger")
585 << "Multi-trigger term pool for " << f
<< " : " << std::endl
;
586 for (size_t i
= 0, psize
= mpats
.size(); i
< psize
; i
++)
588 Trace("auto-gen-trigger") << " " << mpats
[i
] << std::endl
;
595 void InstStrategyAutoGenTriggers::addPatternToPool( Node q
, Node pat
, unsigned num_fv
, Node mpat
) {
596 d_pat_to_mpat
[pat
] = mpat
;
597 unsigned num_vars
= options::partialTriggers() ? d_num_trigger_vars
[q
] : q
[0].getNumChildren();
598 if (num_fv
== num_vars
)
600 d_patTerms
[0][q
].push_back( pat
);
601 d_is_single_trigger
[ pat
] = true;
603 d_patTerms
[1][q
].push_back( pat
);
604 d_is_single_trigger
[ pat
] = false;
609 void InstStrategyAutoGenTriggers::addTrigger( inst::Trigger
* tr
, Node q
) {
614 if (d_num_trigger_vars
[q
] < q
[0].getNumChildren())
616 NodeManager
* nm
= NodeManager::currentNM();
617 // partial trigger : generate implication to mark user pattern
619 d_quantEngine
->getTermUtil()->substituteInstConstantsToBoundVariables(
620 tr
->getInstPattern(), q
);
621 Node ipl
= nm
->mkNode(INST_PATTERN_LIST
, pat
);
622 Node qq
= nm
->mkNode(FORALL
,
623 d_vc_partition
[1][q
],
624 nm
->mkNode(FORALL
, d_vc_partition
[0][q
], q
[1]),
626 Trace("auto-gen-trigger-partial")
627 << "Make partially specified user pattern: " << std::endl
;
628 Trace("auto-gen-trigger-partial") << " " << qq
<< std::endl
;
629 Node lem
= nm
->mkNode(OR
, q
.negate(), qq
);
630 d_quantEngine
->addLemma(lem
);
634 if (tr
->isMultiTrigger())
636 // disable all other multi triggers
637 std::map
<Trigger
*, bool>& agt
= d_auto_gen_trigger
[1][q
];
638 for (std::map
<Trigger
*, bool>::iterator it
= agt
.begin(); it
!= agt
.end();
641 agt
[it
->first
] = false;
649 // making it during an instantiation round, so must reset
650 std::map
<Trigger
*, bool>& agt
= d_auto_gen_trigger
[tindex
][q
];
651 if (agt
.find(tr
) == agt
.end())
653 tr
->resetInstantiationRound();
654 tr
->reset(Node::null());
659 bool InstStrategyAutoGenTriggers::hasUserPatterns( Node q
) {
660 if (q
.getNumChildren() != 3)
664 std::map
<Node
, bool>::iterator it
= d_hasUserPatterns
.find(q
);
665 if (it
!= d_hasUserPatterns
.end())
670 for (const Node
& ip
: q
[2])
672 if (ip
.getKind() == INST_PATTERN
)
678 d_hasUserPatterns
[q
] = hasPat
;
682 void InstStrategyAutoGenTriggers::addUserNoPattern( Node q
, Node pat
) {
683 Assert(pat
.getKind() == INST_NO_PATTERN
&& pat
.getNumChildren() == 1);
684 std::vector
<Node
>& ung
= d_user_no_gen
[q
];
685 if (std::find(ung
.begin(), ung
.end(), pat
[0]) == ung
.end())
687 Trace("user-pat") << "Add user no-pattern: " << pat
[0] << " for " << q
<< std::endl
;
688 ung
.push_back(pat
[0]);
692 } /* CVC4::theory::quantifiers namespace */
693 } /* CVC4::theory namespace */
694 } /* CVC4 namespace */