diff options
author | Andrew Reynolds <andrew.j.reynolds@gmail.com> | 2018-05-18 12:53:53 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-18 12:53:53 -0500 |
commit | 6a94704db9d3e66ed7f54f75a37096e543552866 (patch) | |
tree | e60ba07fc9de20358d446852907074b4cdc8a081 /src/theory | |
parent | 1af890ef4fed0c0151dc2ab954dce0121dd283d8 (diff) |
Unified fairness scheme for cegis unif (#1941)
Diffstat (limited to 'src/theory')
-rw-r--r-- | src/theory/quantifiers/sygus/cegis_unif.cpp | 73 | ||||
-rw-r--r-- | src/theory/quantifiers/sygus/cegis_unif.h | 18 | ||||
-rw-r--r-- | src/theory/quantifiers/sygus/sygus_grammar_cons.cpp | 42 | ||||
-rw-r--r-- | src/theory/quantifiers/sygus/sygus_grammar_cons.h | 9 | ||||
-rw-r--r-- | src/theory/quantifiers/sygus/term_database_sygus.cpp | 10 | ||||
-rw-r--r-- | src/theory/quantifiers/sygus/term_database_sygus.h | 8 |
6 files changed, 137 insertions, 23 deletions
diff --git a/src/theory/quantifiers/sygus/cegis_unif.cpp b/src/theory/quantifiers/sygus/cegis_unif.cpp index 692055b87..57c4c1ad3 100644 --- a/src/theory/quantifiers/sygus/cegis_unif.cpp +++ b/src/theory/quantifiers/sygus/cegis_unif.cpp @@ -330,11 +330,23 @@ void CegisUnifEnumManager::getEnumeratorsForStrategyPt(Node e, std::vector<Node>& es, unsigned index) const { - std::map<Node, StrategyPtInfo>::const_iterator itc = d_ce_info.find(e); - Assert(itc != d_ce_info.end()); - es.insert(es.end(), - itc->second.d_enums[index].begin(), - itc->second.d_enums[index].end()); + // the number of active enumerators is related to the current cost value + unsigned num_enums = d_curr_guq_val.get(); + Assert(num_enums > 0); + if (index == 1) + { + // we always use (cost-1) conditions + num_enums = num_enums - 1; + } + if (num_enums > 0) + { + std::map<Node, StrategyPtInfo>::const_iterator itc = d_ce_info.find(e); + Assert(itc != d_ce_info.end()); + Assert(num_enums <= itc->second.d_enums[index].size()); + es.insert(es.end(), + itc->second.d_enums[index].begin(), + itc->second.d_enums[index].begin() + num_enums); + } } void CegisUnifEnumManager::registerEvalPts(const std::vector<Node>& eis, Node e) @@ -469,6 +481,57 @@ void CegisUnifEnumManager::incrementNumEnumerators() registerEvalPtAtSize(c, ei, new_lit, new_size); } } + // enforce fairness between number of enumerators and enumerator size + if (new_size > 1) + { + // construct the "virtual enumerator" + if (d_virtual_enum.isNull()) + { + // we construct the default integer grammar with no variables, e.g.: + // A -> 0 | 1 | A+A + TypeNode intTn = nm->integerType(); + // use a null variable list + Node bvl; + std::stringstream ss; + ss << "_virtual_enum_grammar"; + std::string virtualEnumName(ss.str()); + std::map<TypeNode, std::vector<Node>> extra_cons; + std::map<TypeNode, std::vector<Node>> exclude_cons; + // do not include "-", which is included by default for integers + exclude_cons[intTn].push_back(nm->operatorOf(MINUS)); + std::unordered_set<Node, NodeHashFunction> term_irrelevant; + TypeNode vtn = + CegGrammarConstructor::mkSygusDefaultType(intTn, + bvl, + virtualEnumName, + extra_cons, + exclude_cons, + term_irrelevant); + d_virtual_enum = nm->mkSkolem("_ve", vtn); + d_tds->registerEnumerator(d_virtual_enum, Node::null(), d_parent); + } + // if new_size is a power of two, then isPow2 returns log2(new_size)+1 + // otherwise, this returns 0. In the case it returns 0, we don't care + // since the floor( log2( i ) ) = floor( log2( i - 1 ) ) and we do not + // increase our size bound. + unsigned pow_two = Integer(new_size).isPow2(); + if (pow_two > 0) + { + Node size_ve = nm->mkNode(DT_SIZE, d_virtual_enum); + Node fair_lemma = + nm->mkNode(GEQ, size_ve, nm->mkConst(Rational(pow_two - 1))); + fair_lemma = nm->mkNode(OR, new_lit, fair_lemma); + Trace("cegis-unif-enum-lemma") + << "CegisUnifEnum::lemma, fairness size:" << fair_lemma << "\n"; + // this lemma relates the number of conditions we enumerate and the + // maximum size of a term that is part of our solution. It is of the + // form: + // G_uq_i => size(ve) >= log_2( i-1 ) + // In other words, if we use i conditions, then we allow terms in our + // solution whose size is at most log_2(i-1). + d_qe->getOutputChannel().lemma(fair_lemma); + } + } } } diff --git a/src/theory/quantifiers/sygus/cegis_unif.h b/src/theory/quantifiers/sygus/cegis_unif.h index 5c2c11e4d..80c11f82d 100644 --- a/src/theory/quantifiers/sygus/cegis_unif.h +++ b/src/theory/quantifiers/sygus/cegis_unif.h @@ -142,6 +142,24 @@ class CegisUnifEnumManager std::map<unsigned, Node> d_guq_lit; /** Have we returned a decision in the current SAT context? */ context::CDO<bool> d_ret_dec; + /** the "virtual" enumerator + * + * This enumerator is used for enforcing fairness. In particular, we relate + * its size to the number of conditions allocated by this class such that: + * ~G_uq_i => size(d_virtual_enum) >= floor( log2( i-1 ) ) + * In other words, if we are using (i-1) conditions in our solution, + * the size of the virtual enumerator is at least the floor of the log (base + * two) of (i-1). Due to the default fairness scheme in the quantifier-free + * datatypes solver (if --sygus-fair-max is enabled), this ensures that other + * enumerators are allowed to have at least this size. This affect other + * fairness schemes in an analogous fashion. In particular, we enumerate + * based on the tuples for (term size, #conditions): + * (0,0), (0,1) [size 0] + * (0,2), (0,3), (1,1), (1,2), (1,3) [size 1] + * (0,4), ..., (0,7), (1,4), ..., (1,7), (2,0), ..., (2,7) [size 2] + * (0,8), ..., (0,15), (1,8), ..., (1,15), ... [size 3] + */ + Node d_virtual_enum; /** * The minimal n such that G_uq_n is not asserted negatively in the * current SAT context. diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp index 412d1b211..3d8052ae4 100644 --- a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp +++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp @@ -99,6 +99,7 @@ Node CegGrammarConstructor::process(Node q, Trace("cegqi") << "CegConjecture : collect constants..." << std::endl; collectTerms( q[1], extra_cons ); } + std::map<TypeNode, std::vector<Node>> exc_cons; NodeManager* nm = NodeManager::currentNM(); @@ -140,20 +141,17 @@ Node CegGrammarConstructor::process(Node q, // check which arguments are irrelevant std::unordered_set<unsigned> arg_irrelevant; d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant); - std::unordered_set<Node, NodeHashFunction> term_irrelevant; + std::unordered_set<Node, NodeHashFunction> term_irlv; // convert to term - for (std::unordered_set<unsigned>::iterator ita = arg_irrelevant.begin(); - ita != arg_irrelevant.end(); - ++ita) + for (const unsigned& arg : arg_irrelevant) { - unsigned arg = *ita; Assert(arg < sfvl.getNumChildren()); - term_irrelevant.insert(sfvl[arg]); + term_irlv.insert(sfvl[arg]); } // make the default grammar tn = mkSygusDefaultType( - preGrammarType, sfvl, ss.str(), extra_cons, term_irrelevant); + preGrammarType, sfvl, ss.str(), extra_cons, exc_cons, term_irlv); } // normalize type @@ -389,7 +387,8 @@ void CegGrammarConstructor::mkSygusDefaultGrammar( TypeNode range, Node bvl, const std::string& fun, - std::map<TypeNode, std::vector<Node> >& extra_cons, + std::map<TypeNode, std::vector<Node>>& extra_cons, + std::map<TypeNode, std::vector<Node>>& exc_cons, std::unordered_set<Node, NodeHashFunction>& term_irrelevant, std::vector<CVC4::Datatype>& datatypes, std::set<Type>& unres) @@ -589,8 +588,18 @@ void CegGrammarConstructor::mkSygusDefaultGrammar( } Trace("sygus-grammar-def") << "...make datatype " << datatypes[i] << std::endl; datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true ); - for( unsigned j=0; j<ops[i].size(); j++ ){ - datatypes[i].addSygusConstructor( ops[i][j], cnames[j], cargs[j] ); + std::map<TypeNode, std::vector<Node>>::iterator itexc = + exc_cons.find(types[i]); + for (unsigned j = 0, size = ops[i].size(); j < size; j++) + { + // add the constructor if it is not excluded + Node opn = Node::fromExpr(ops[i][j]); + if (itexc == exc_cons.end() + || std::find(itexc->second.begin(), itexc->second.end(), opn) + == itexc->second.end()) + { + datatypes[i].addSygusConstructor(ops[i][j], cnames[j], cargs[j]); + } } Trace("sygus-grammar-def") << "...built datatype " << datatypes[i] << " "; @@ -717,7 +726,8 @@ TypeNode CegGrammarConstructor::mkSygusDefaultType( TypeNode range, Node bvl, const std::string& fun, - std::map<TypeNode, std::vector<Node> >& extra_cons, + std::map<TypeNode, std::vector<Node>>& extra_cons, + std::map<TypeNode, std::vector<Node>>& exclude_cons, std::unordered_set<Node, NodeHashFunction>& term_irrelevant) { Trace("sygus-grammar-def") << "*** Make sygus default type " << range << ", make datatypes..." << std::endl; @@ -726,8 +736,14 @@ TypeNode CegGrammarConstructor::mkSygusDefaultType( } std::set<Type> unres; std::vector< CVC4::Datatype > datatypes; - mkSygusDefaultGrammar( - range, bvl, fun, extra_cons, term_irrelevant, datatypes, unres); + mkSygusDefaultGrammar(range, + bvl, + fun, + extra_cons, + exclude_cons, + term_irrelevant, + datatypes, + unres); Trace("sygus-grammar-def") << "...made " << datatypes.size() << " datatypes, now make mutual datatype types..." << std::endl; Assert( !datatypes.empty() ); std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.h b/src/theory/quantifiers/sygus/sygus_grammar_cons.h index 39f95527f..578325fea 100644 --- a/src/theory/quantifiers/sygus/sygus_grammar_cons.h +++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.h @@ -67,7 +67,8 @@ public: /** make the default sygus datatype type corresponding to builtin type range * bvl is the set of free variables to include in the grammar * fun is for naming - * extra_cons is a set of extra constant symbols to include in the grammar + * extra_cons is a set of extra constant symbols to include in the grammar, + * exclude_cons is used to exclude operators from the grammar, * term_irrelevant is a set of terms that should not be included in the * grammar. */ @@ -76,6 +77,7 @@ public: Node bvl, const std::string& fun, std::map<TypeNode, std::vector<Node> >& extra_cons, + std::map<TypeNode, std::vector<Node> >& exclude_cons, std::unordered_set<Node, NodeHashFunction>& term_irrelevant); /** make the default sygus datatype type corresponding to builtin type range */ static TypeNode mkSygusDefaultType(TypeNode range, @@ -83,8 +85,10 @@ public: const std::string& fun) { std::map<TypeNode, std::vector<Node> > extra_cons; + std::map<TypeNode, std::vector<Node> > exclude_cons; std::unordered_set<Node, NodeHashFunction> term_irrelevant; - return mkSygusDefaultType(range, bvl, fun, extra_cons, term_irrelevant); + return mkSygusDefaultType( + range, bvl, fun, extra_cons, exclude_cons, term_irrelevant); } /** make the sygus datatype type that encodes the solution space (lambda * templ_arg. templ[templ_arg]) where templ_arg @@ -138,6 +142,7 @@ public: Node bvl, const std::string& fun, std::map<TypeNode, std::vector<Node> >& extra_cons, + std::map<TypeNode, std::vector<Node> >& exclude_cons, std::unordered_set<Node, NodeHashFunction>& term_irrelevant, std::vector<CVC4::Datatype>& datatypes, std::set<Type>& unres); diff --git a/src/theory/quantifiers/sygus/term_database_sygus.cpp b/src/theory/quantifiers/sygus/term_database_sygus.cpp index 85ff5c896..5efa1198b 100644 --- a/src/theory/quantifiers/sygus/term_database_sygus.cpp +++ b/src/theory/quantifiers/sygus/term_database_sygus.cpp @@ -752,8 +752,14 @@ void TermDbSygus::registerEnumerator(Node e, CegConjecture* conj, bool mkActiveGuard) { - Assert(d_enum_to_conjecture.find(e) == d_enum_to_conjecture.end()); - Trace("sygus-db") << "Register measured term : " << e << std::endl; + if (d_enum_to_conjecture.find(e) != d_enum_to_conjecture.end()) + { + // already registered + return; + } + Trace("sygus-db") << "Register enumerator : " << e << std::endl; + // register its type + registerSygusType(e.getType()); d_enum_to_conjecture[e] = conj; d_enum_to_synth_fun[e] = f; if( mkActiveGuard ){ diff --git a/src/theory/quantifiers/sygus/term_database_sygus.h b/src/theory/quantifiers/sygus/term_database_sygus.h index a9e17fdf9..d53e436e0 100644 --- a/src/theory/quantifiers/sygus/term_database_sygus.h +++ b/src/theory/quantifiers/sygus/term_database_sygus.h @@ -38,7 +38,13 @@ class TermDbSygus { bool reset(Theory::Effort e); /** Identify this utility */ std::string identify() const { return "TermDbSygus"; } - /** register the sygus type */ + /** register the sygus type + * + * This initializes this database for sygus datatype type tn. This may + * throw an assertion failure if the sygus grammar has type errors. Otherwise, + * after registering a sygus type, the query functions in this class (such + * as sygusToBuiltinType, getKindConsNum, etc.) can be called for tn. + */ void registerSygusType(TypeNode tn); //------------------------------utilities |