/** * Compose auxiliary functions recursively into a BDD. */ void BddTrAttachment::composeAuxiliaryFunctions( BddAttachment const * bat, BDD & f, unordered_map<int, ID> const & index2id) { vector<unsigned int> support = f.SupportIndices(); queue< vector<unsigned int> > q; q.push(support); while (!q.empty()) { vector<unsigned int> supp = q.front(); q.pop(); for (vector<unsigned int>::const_iterator j = supp.begin(); j != supp.end(); ++j) { unordered_map<int,ID>::const_iterator cit = index2id.find(*j); if (cit != index2id.end()) { BDD g = bat->bdd(cit->second); BDD a = bddManager().bddVar(*j); f = f.AndAbstract(a.Xnor(g),a); vector<unsigned int> addSupp = g.SupportIndices(); q.push(addSupp); } } } }
/** * Build BDDs for the transition relation of the model. */ void BddTrAttachment::build() { Options::Verbosity verbosity = _model.verbosity(); if (verbosity > Options::Silent) cout << "Computing transition relation for model " << _model.name() << endl; BddAttachment const * bat = (BddAttachment const *) _model.constAttachment(Key::BDD); assert(bat != 0); // The BDD attachment may not have BDDs because of a timeout. if (bat->hasBdds() == false) { _model.constRelease(bat); return; } if (hasBdds()) return; ExprAttachment const * eat = (ExprAttachment *) _model.constAttachment(Key::EXPR); Expr::Manager::View *v = _model.newView(); resetBddManager("bdd_tr_timeout"); try { // Gather the conjuncts of the transition relation and initial condition. const vector<ID> sv = eat->stateVars(); const vector<ID> iv = eat->inputs(); const unordered_map<ID, int>& auxVar = bat->auxiliaryVars(); int nvars = 2 * sv.size() + iv.size() + auxVar.size(); if (verbosity > Options::Terse) cout << "number of variables = " << nvars << endl; BDD inputCube = bddManager().bddOne(); for (vector<ID>::const_iterator i = iv.begin(); i != iv.end(); ++i) { BDD w = bat->bdd(*i); inputCube &= w; } BDD fwAbsCube = bddManager().bddOne(); BDD bwAbsCube = bddManager().bddOne(); vector<BDD> conjuncts; for (vector<ID>::const_iterator i = sv.begin(); i != sv.end(); ++i) { BDD x = bat->bdd(*i); _xvars.push_back(x); fwAbsCube &= x; ID nsv = v->prime(*i); BDD y = bat->bdd(nsv); _yvars.push_back(y); bwAbsCube &= y; ID nsf = eat->nextStateFnOf(*i); BDD f = bat->bdd(nsf); if (verbosity > Options::Informative) if (f.IsVar()) cout << "Found a variable next-state function" << endl; BDD t = y.Xnor(f); if (verbosity > Options::Verbose) reportBDD(stringOf(*v, nsv) + " <-> " + stringOf(*v, nsf), t, nvars, verbosity, Options::Verbose); conjuncts.push_back(t); } // Process auxiliary variables. vector<BDD> conjAux; for (unordered_map<ID, int>::const_iterator it = auxVar.begin(); it != auxVar.end(); ++it) { BDD f = bat->bdd(it->first); BDD v = bddManager().bddVar(it->second); BDD t = v.Xnor(f); conjAux.push_back(t); inputCube &= v; } conjuncts.insert(conjuncts.end(), conjAux.begin(), conjAux.end()); // Build map from BDD variable indices to expression IDs. unordered_map<int, ID> index2id; for (unordered_map<ID, int>::const_iterator it = auxVar.begin(); it != auxVar.end(); ++it) { index2id[it->second] = it->first; } // Add invariant constraints. const vector<ID> constr = eat->constraintFns(); for (vector<ID>::const_iterator i = constr.begin(); i != constr.end(); ++i) { BDD cn = bat->bdd(*i); composeAuxiliaryFunctions(bat, cn, index2id); _constr.push_back(cn); BDD cny = cn.ExistAbstract(inputCube); cny = cny.SwapVariables(_yvars, _xvars); reportBDD("Invariant constraint", cn, nvars, verbosity, Options::Terse); conjuncts.push_back(cn); conjuncts.push_back(cny); } unsigned int clusterLimit = 2500; if (_model.options().count("bdd_tr_cluster")) { clusterLimit = _model.options()["bdd_tr_cluster"].as<unsigned int>(); } // Collect output functions applying invariant constraints and substituting // auxiliary variables. const vector<ID> outf = eat->outputs(); for (vector<ID>::const_iterator i = outf.begin(); i != outf.end(); ++i) { BDD of = bat->bdd(eat->outputFnOf(*i)); // Apply invariant constraints. for (vector<BDD>::iterator i = _constr.begin(); i != _constr.end(); ++i) of &= *i; composeAuxiliaryFunctions(bat, of, index2id); // Finally, remove primary inputs. if (_model.defaultMode() != Model::mFAIR) { of = of.ExistAbstract(inputCube); } _inv.push_back(of); reportBDD("Output function", of, _xvars.size(), verbosity, Options::Terse); } // Build the transition relation from the conjuncts. vector<BDD> sortedConjuncts = linearArrangement(conjuncts); assert(sortedConjuncts.size() == conjuncts.size()); conjuncts.clear(); vector<BDD> clusteredConjuncts = clusterConjuncts(sortedConjuncts, inputCube, clusterLimit, verbosity); assert(sortedConjuncts.size() == 0); inputCube = quantifyLocalInputs(clusteredConjuncts, inputCube, clusterLimit, verbosity); computeSchedule(clusteredConjuncts, inputCube); // Build initial condition. const vector<ID> ini = eat->initialConditions(); _init = bddManager().bddOne(); for (vector<ID>::const_iterator i = ini.begin(); i != ini.end(); ++i) { BDD ic = bat->bdd(*i); _init &= ic; } reportBDD("Initial condition", _init, _xvars.size(), verbosity, Options::Terse); } catch (Timeout& e) { bddManager().ClearErrorCode(); bddManager().UnsetTimeLimit(); bddManager().ResetStartTime(); if (verbosity > Options::Silent) std::cout << e.what() << std::endl; _tr.clear(); _xvars.clear(); _yvars.clear(); _inv.clear(); _prequantx = bddManager().bddOne(); _prequanty = bddManager().bddOne(); _init = bddManager().bddZero(); } delete v; _model.constRelease(eat); _model.constRelease(bat); bddManager().UpdateTimeLimit(); } // BddTrAttachment::build