/* Copyright (c) 2012-2018 Artur Meski */ #include "symrs.hh" #include "rs.hh" #include "stateconstr.hh" #include "bdd_macro.hh" SymRS::SymRS(RctSys *rs, Options *opts) { this->rs = rs; this->opts = opts; mapProcEntities(); totalEntities = rs->getEntitiesSize(); // TODO: remove totalActions = 0; totalRctSysStateVars = getTotalProductVariables(); totalCtxEntities = getTotalCtxEntitiesVariables(); totalCtxAutStateVars = getCtxAutStateEncodingSize(); totalStateVars = totalRctSysStateVars + totalCtxAutStateVars; numberOfProc = rs->getNumberOfProcesses(); partTrans = nullptr; monoTrans = nullptr; pv_ca = nullptr; pv_ca_succ = nullptr; tr_ca = nullptr; // TODO: this should be triggered by the parser and it should depend // on an option in the input file rs->ctx_aut->makeProgressive(); rs->printSystem(); encode(); } bool SymRS::usingContextAutomaton(void) { return rs->ctx_aut != nullptr; } BDD SymRS::encEntity(std::string proc_name, std::string entity_name) const { return encEntity(rs->getProcessID(proc_name), rs->getEntityID(entity_name)); } void SymRS::encode(void) { VERB("Encoding..."); if (opts->measure) { opts->enc_time = cpuTime(); opts->enc_mem = memUsed(); } initBDDvars(); if (usingContextAutomaton()) { encodeCtxAutTrans(); } else { VERB_LN(3, "Not using context automata, not encoding TR for CA") } encodeTransitions(); encodeInitStates(); if (opts->measure) { opts->enc_time = cpuTime() - opts->enc_time; opts->enc_mem = memUsed() - opts->enc_mem; } VERB("Encoding done"); } LocalIndicesForProcEntities SymRS::buildLocalEntitiesMap( const EntitiesForProc &procEnt) { LocalIndicesForProcEntities ent_map; for (const auto &proc_ent : procEnt) { Process proc_id = proc_ent.first; unsigned int cnt = 0; for (const auto &e : proc_ent.second) { ent_map[proc_id][e] = cnt++; } } return ent_map; } void SymRS::initBDDvars(void) { VERB("Initialising CUDD"); cuddMgr = new Cudd(0, 0); VERB("Preparing BDD variables"); // used for bddVar unsigned int bdd_var_idx = 0; // used for pv, pv_succ unsigned int global_state_idx = 0; // ---------------------------------------------------------- // Global state // ---------------------------------------------------------- // // Variables for reaction system with CA (if used) // pv = new BDDvec(totalStateVars); pv_succ = new BDDvec(totalStateVars); pv_E = new BDD(BDD_TRUE); pv_succ_E = new BDD(BDD_TRUE); // ---------------------------------------------------------- // Distributed RS // ---------------------------------------------------------- pv_drs = new vector(numberOfProc); pv_drs_succ = new vector(numberOfProc); pv_drs_flat = new BDDvec(totalRctSysStateVars); pv_drs_flat_succ = new BDDvec(totalRctSysStateVars); pv_drs_E = new BDDvec(numberOfProc); pv_drs_flat_E = new BDD(BDD_TRUE); pv_drs_flat_succ_E = new BDD(BDD_TRUE); VERB_LN(2, "DRS processing"); unsigned int drs_flat_index = 0; for (const auto &proc_ent : usedProducts) { auto proc_id = proc_ent.first; auto entities_count = proc_ent.second.size(); // // The order of entities and their correspondence to the // correct entities in pv (global) does not matter here. // // The correspondence is established in the methods // returning BDD variables (encoding) of the individual // enties. // // We only need to make sure we have the correct number of // variables that are going to be used in the encoding. // // For efficiency, we do not introduce BDD variables for // entites that are never produced in a given local component. // Instead, we only select those that are. We apply the same // strategy to product entities and context entities. // // First, we need to adjust the sizes of all the nested vectors (*pv_drs)[proc_id].resize(entities_count); (*pv_drs_succ)[proc_id].resize(entities_count); (*pv_drs_E)[proc_id] = BDD_TRUE; for (unsigned int i = 0; i < entities_count; ++i) { assert(drs_flat_index < totalRctSysStateVars); assert(global_state_idx < totalStateVars); assert(proc_id < numberOfProc); assert(i < totalEntities); // Variables for each individual process/component (*pv_drs)[proc_id][i] = cuddMgr->bddVar(bdd_var_idx++); (*pv_drs_succ)[proc_id][i] = cuddMgr->bddVar(bdd_var_idx++); // Quantification (per proc) (*pv_drs_E)[proc_id] *= (*pv_drs)[proc_id][i]; // The DRS part of the system (flattened): these vars do not include CA (*pv_drs_flat)[drs_flat_index] = (*pv_drs)[proc_id][i]; (*pv_drs_flat_succ)[drs_flat_index] = (*pv_drs_succ)[proc_id][i]; *pv_drs_flat_E *= (*pv_drs_flat)[drs_flat_index]; *pv_drs_flat_succ_E *= (*pv_drs_flat_succ)[drs_flat_index]; // Variables used for the global states (*pv)[global_state_idx] = (*pv_drs)[proc_id][i]; (*pv_succ)[global_state_idx] = (*pv_drs_succ)[proc_id][i]; *pv_E *= (*pv)[global_state_idx]; *pv_succ_E *= (*pv_succ)[global_state_idx]; ++drs_flat_index; ++global_state_idx; } } // ---------------------------------------------------------- // Context Automaton // ---------------------------------------------------------- if (usingContextAutomaton()) { VERB_LN(2, "Context automaton variables"); pv_ca = new BDDvec(totalCtxAutStateVars); pv_ca_succ = new BDDvec(totalCtxAutStateVars); pv_ca_E = new BDD(BDD_TRUE); pv_ca_succ_E = new BDD(BDD_TRUE); for (unsigned int i = 0; i < totalCtxAutStateVars; ++i) { (*pv_ca)[i] = cuddMgr->bddVar(bdd_var_idx++); (*pv_ca_succ)[i] = cuddMgr->bddVar(bdd_var_idx++); *pv_ca_E *= (*pv_ca)[i]; *pv_ca_succ_E *= (*pv_ca_succ)[i]; (*pv)[global_state_idx] = (*pv_ca)[i]; (*pv_succ)[global_state_idx] = (*pv_ca_succ)[i]; ++global_state_idx; } } // ---------------------------------------------------------- // Enabledness of processes // ---------------------------------------------------------- // // These variables indicate which process is // allowed to perform action // VERB_LN(2, "Variables for process enabledness/activity"); pv_proc_enab = new BDDvec(numberOfProc); pv_proc_enab_E = new BDD(BDD_TRUE); for (unsigned int i = 0; i < numberOfProc; ++i) { auto bdd_var = cuddMgr->bddVar(bdd_var_idx++); (*pv_proc_enab)[i] = bdd_var; *pv_proc_enab_E *= bdd_var; } // ---------------------------------------------------------- // Context Entities // ---------------------------------------------------------- VERB_LN(2, "Variables for context entities"); pv_ctx = new BDDvec(totalCtxEntities); pv_ctx_E = new BDD(BDD_TRUE); pv_proc_ctx = new vector(numberOfProc); pv_proc_ctx_E = new BDDvec(numberOfProc); unsigned int flat_ctx_index = 0; for (const auto &proc_ent : usedCtxEntities) { auto proc_id = proc_ent.first; auto entities_count = proc_ent.second.size(); assert(entities_count < totalEntities); // adjust the size of the nested vector before we use an index (*pv_proc_ctx)[proc_id].resize(entities_count); (*pv_proc_ctx_E)[proc_id] = BDD_TRUE; for (unsigned int i = 0; i < entities_count; ++i) { assert(flat_ctx_index < totalCtxEntities); assert(proc_id < numberOfProc); (*pv_proc_ctx)[proc_id][i] = cuddMgr->bddVar(bdd_var_idx++); (*pv_proc_ctx_E)[proc_id] *= (*pv_proc_ctx)[proc_id][i]; (*pv_ctx)[flat_ctx_index] = (*pv_proc_ctx)[proc_id][i]; *pv_ctx_E *= (*pv_ctx)[flat_ctx_index]; ++flat_ctx_index; } } if (usingContextAutomaton()) { VERB_LN(2, "Updating quantification BDDs with context automaton") *pv_E *= *pv_ca_E; *pv_succ_E *= *pv_ca_succ_E; } VERB("All BDD variables ready"); } size_t SymRS::getTotalProductVariables(void) { size_t total = 0; for (const auto &it : usedProducts) { total += it.second.size(); } return total; } size_t SymRS::getTotalCtxEntitiesVariables(void) { size_t total = 0; for (const auto &it : usedCtxEntities) { total += it.second.size(); } return total; } void SymRS::mapProcEntities(void) { // // Reactions // for (const auto &proc_rcts : rs->proc_reactions) { Process proc_id = proc_rcts.first; for (const auto &rct : proc_rcts.second) { // collect entities that can be produced // locally by the process with proc_id SET_ADD(usedProducts[proc_id], rct.prod); } } prod_ent_local_idx = buildLocalEntitiesMap(usedProducts); // // Context automaton // for (const auto &tr : rs->ctx_aut->transitions) { for (const auto &proc_ctx : tr.ctx) { Process proc_id = proc_ctx.first; SET_ADD(usedCtxEntities[proc_id], proc_ctx.second); } } ctx_ent_local_idx = buildLocalEntitiesMap(usedCtxEntities); if (opts->verbose > 9) { cout << "Used product entities:" << endl; cout << rs->procEntitiesToStr(usedProducts) << endl; cout << "Used context entities:" << endl; cout << rs->procEntitiesToStr(usedCtxEntities) << endl; } } unsigned int SymRS::getLocalProductEntityIndex(Process proc_id, Entity entity) const { assert(productEntityExists(proc_id, entity)); auto idx = prod_ent_local_idx.at(proc_id).at(entity); assert(idx < prod_ent_local_idx.at(proc_id).size()); return idx; } unsigned int SymRS::getLocalCtxEntityIndex(Process proc_id, Entity entity) const { assert(ctxEntityExists(proc_id, entity)); auto idx = ctx_ent_local_idx.at(proc_id).at(entity); assert(idx < ctx_ent_local_idx.at(proc_id).size()); return idx; } BDD SymRS::encEntity_raw(Process proc_id, Entity entity, bool succ) const { BDD r; assert(proc_id < numberOfProc); assert(productEntityExists(proc_id, entity)); auto local_entity_id = getLocalProductEntityIndex(proc_id, entity); if (succ) { r = (*pv_drs_succ)[proc_id][local_entity_id]; } else { r = (*pv_drs)[proc_id][local_entity_id]; } return r; } BDD SymRS::encCtxEntity(Process proc_id, Entity entity) const { assert(entity < totalEntities); assert(proc_id < numberOfProc); assert(ctxEntityExists(proc_id, entity)); auto local_entity_id = getLocalCtxEntityIndex(proc_id, entity); return (*pv_proc_ctx)[proc_id][local_entity_id]; } bool SymRS::productEntityExists(Process proc_id, Entity entity) const { if (prod_ent_local_idx.count(proc_id) == 0) { return false; } else { if (prod_ent_local_idx.at(proc_id).count(entity) == 1) { return true; } } return false; } bool SymRS::ctxEntityExists(Process proc_id, Entity entity) const { if (ctx_ent_local_idx.count(proc_id) == 0) { return false; } else { if (ctx_ent_local_idx.at(proc_id).count(entity) == 1) { return true; } } return false; } bool SymRS::processUsesEntity(Process proc_id, Entity entity_id) const { if (productEntityExists(proc_id, entity_id) || ctxEntityExists(proc_id, entity_id)) { return true; } return false; } BDD SymRS::encEntitiesConj_raw(Process proc_id, const Entities &entities, bool succ) { BDD r = BDD_TRUE; for (const auto &entity : entities) { if (succ) { r *= encEntitySucc(proc_id, entity); } else { r *= encEntity(proc_id, entity); } } return r; } BDD SymRS::encEntitiesDisj_raw(Process proc_id, const Entities &entities, bool succ) { BDD r = BDD_FALSE; for (const auto &entity : entities) { if (succ) { r += encEntitySucc(proc_id, entity); } else { r += encEntity(proc_id, entity); } } return r; } BDD SymRS::encEntityCondition(Process proc_id, Entity entity_id) { // // Here we encode an entity-based condition which uses // the entity appearing as a product or a context entity. // BDD r = BDD_FALSE; if (productEntityExists(proc_id, entity_id)) { r += encEntity(proc_id, entity_id); } if (ctxEntityExists(proc_id, entity_id)) { r += encCtxEntity(proc_id, entity_id); } // pointless call to this function -- we should have prevented it: assert(r != BDD_FALSE); return r; } BDD SymRS::encContext(const EntitiesForProc &proc_entities) { BDD r = BDD_TRUE; for (const auto &pe : proc_entities) { auto proc_id = pe.first; auto entities = pe.second; for (const auto &entity : entities) { r *= encCtxEntity(proc_id, entity); } r *= encProcEnabled(proc_id); } return r; } BDD SymRS::compState(const BDD &state) const { assert(0); BDD s = state; for (unsigned int i = 0; i < totalRctSysStateVars; ++i) { if (!(*pv)[i] * state != cuddMgr->bddZero()) { s *= !(*pv)[i]; } } return s; } BDD SymRS::compContext(const BDD &context) const { BDD c = context; for (const auto &var : *pv_ctx) { if (!var * context != cuddMgr->bddZero()) { c *= !var; } } for (const auto &var : *pv_proc_enab) { if (!var * context != cuddMgr->bddZero()) { c *= !var; } } return c; } std::string SymRS::decodedRctSysStateToStr(const BDD &state) { std::string s = "{ "; for (const auto &proc_entities : usedProducts) { auto proc_id = proc_entities.first; auto entities = proc_entities.second; s += rs->getProcessName(proc_id) + "={ "; for (const auto &entity : entities) { if (!(encEntity(proc_id, entity) * state).IsZero()) { s += rs->entityToStr(entity) + " "; } } s += "} "; } s += "}"; return s; } void SymRS::printDecodedRctSysStates(const BDD &states) { BDD unproc = states; while (!unproc.IsZero()) { BDD t = unproc.PickOneMinterm(*pv_drs_flat); cout << decodedRctSysStateToStr(t) << endl; if (opts->verbose > 9) { BDD_PRINT(t); cout << endl; } unproc -= t; } } DecompReactions SymRS::getProductionConditions(Process proc_id) { DecompReactions dr; for (const auto &rct : rs->proc_reactions[proc_id]) { ReactionCond cond; cond.rctt = rct.rctt; cond.inhib = rct.inhib; for (const auto &prod : rct.prod) { dr[prod].push_back(cond); } } return dr; } BDD SymRS::encEnabledness(Process prod_proc_id, Entity entity_id) { assert(prod_conds.size() > prod_proc_id); BDD enab = BDD_FALSE; auto production_conditions = prod_conds[prod_proc_id][entity_id]; VERB_LN(5, "| Produce " << rs->getEntityName(entity_id) << " in " << rs->getProcessName(prod_proc_id) << ":"); // Iterate through production conditions for the entity (entity_id) that // belongs to the process prod_proc_id which contain: // - reactants // - inhibitors // // Here we are building an alternative for the enab BDD, // for all the possible production conditions // for (const auto &cond : production_conditions) { // Take all the reactants... (conjuntion) BDD reactants = BDD_TRUE; for (const auto &reactant : cond.rctt) { // Disjunction for all the processes BDD proc_reactants = BDD_FALSE; if (ctxEntityExists(prod_proc_id, reactant)) { proc_reactants += encCtxEntity(prod_proc_id, reactant); } for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) { if (productEntityExists(proc_id, reactant)) { proc_reactants += encProcEnabled(proc_id) * encEntity(proc_id, reactant); VERB_LN(5, "| - if process " << rs->getProcessName(proc_id) << " is enabled and has " << rs->getEntityName(reactant)); } } // END FOR: prod_id reactants *= proc_reactants; } // END FOR: reactant // Take all the inhibitors... (conjunction) BDD inhibitors = BDD_TRUE; for (const auto &inhibitor : cond.inhib) { // Conjunction for all the processes BDD proc_inhibitors = BDD_TRUE; if (ctxEntityExists(prod_proc_id, inhibitor)) { proc_inhibitors *= !encCtxEntity(prod_proc_id, inhibitor); } for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) { if (productEntityExists(proc_id, inhibitor)) { proc_inhibitors *= !encEntity(proc_id, inhibitor) + !encProcEnabled(proc_id); } } inhibitors *= proc_inhibitors; } enab += reactants * inhibitors; } // END FOR: cond reorder(); return enab; } // BDD SymRS::encEnabledness(Process prod_proc_id, Entity entity_id) // { // assert(prod_conds.size() > prod_proc_id); // BDD enab = BDD_FALSE; // auto production_conditions = prod_conds[prod_proc_id][entity_id]; // VERB_LN(5, "| Produce " << rs->getEntityName(entity_id) << " in " << rs->getProcessName(prod_proc_id) << ":"); // for (const auto &cond : production_conditions) { // BDD reactants = BDD_TRUE; // BDD inhibitors = BDD_TRUE; // for (const auto &reactant : cond.rctt) { // BDD proc_reactants = BDD_FALSE; // for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) { // if (processUsesEntity(proc_id, reactant)) { // proc_reactants += encProcEnabled(proc_id) * encEntityCondition(proc_id, reactant); // VERB_LN(5, "| - if process " << rs->getProcessName(proc_id) << " is enabled and has " << rs->getEntityName(reactant)); // } // } // END FOR: prod_id // reactants *= proc_reactants; // } // END FOR: reactant // // For inhibitors, we take all the processes first and then we iterate over the inhibitors // for (unsigned int proc_id = 0; proc_id < numberOfProc; ++proc_id) { // BDD proc_inhibitors = BDD_TRUE; // for (const auto &inhibitor : cond.inhib) { // if (processUsesEntity(proc_id, inhibitor)) { // proc_inhibitors *= !encEntityCondition(proc_id, inhibitor); // } // } // if (proc_inhibitors != BDD_TRUE) { // just an optimisation // proc_inhibitors += !encProcEnabled(proc_id); // inhibitors *= proc_inhibitors; // } // } // enab += reactants * inhibitors; // } // END FOR: cond // if (opts->reorder_trans) { // VERB_L2("Reordering"); // Cudd_ReduceHeap(cuddMgr->getManager(), CUDD_REORDER_SIFT, 10000); // } // return enab; // } BDD SymRS::encEntitySameSuccessor(Process proc_id, Entity entity_id) { return BDD_IFF(encEntity(proc_id, entity_id), encEntitySucc(proc_id, entity_id)); } BDD SymRS::encEntityProduction(Process proc_id, Entity entity_id) { BDD enabled = encEnabledness(proc_id, entity_id); BDD when_produced = enabled * encEntitySucc(proc_id, entity_id); BDD when_not_produced = !enabled * !encEntitySucc(proc_id, entity_id); BDD proc_enabled = encProcEnabled(proc_id) * (when_produced + when_not_produced); BDD proc_disabled = !encProcEnabled(proc_id) * encEntitySameSuccessor(proc_id, entity_id); BDD result = proc_enabled + proc_disabled; return result; } void SymRS::encodeTransitions(void) { VERB("Decomposing reactions"); prod_conds.resize(numberOfProc); for (auto proc_id = 0; proc_id < numberOfProc; ++proc_id) { prod_conds[proc_id] = getProductionConditions(proc_id); } VERB("Encoding reactions"); if (opts->part_tr_rel) { VERB("Using partitioned transition relation encoding"); if (usingContextAutomaton()) { partTrans = new BDDvec(numberOfProc+1); } else { partTrans = new BDDvec(numberOfProc); } } else { VERB("Using monolithic transition relation encoding"); monoTrans = new BDD(BDD_TRUE); } VERB_LN(3, "Entity production encoding for all the processes and their products"); if (opts->part_tr_rel) { for (const auto &proc_products : usedProducts) { auto proc_id = proc_products.first; auto products = proc_products.second; (*partTrans)[proc_id] = BDD_TRUE; for (const auto &prod : products) { (*partTrans)[proc_id] *= encEntityProduction(proc_id, prod); } } } else { for (const auto &proc_products : usedProducts) { auto proc_id = proc_products.first; auto products = proc_products.second; for (const auto &prod : products) { *monoTrans *= encEntityProduction(proc_id, prod); } } } VERB("Reactions ready"); if (usingContextAutomaton()) { VERB("Augmenting transition relation encoding with the transition relation for context automaton"); if (opts->part_tr_rel) { auto last_index = numberOfProc; (*partTrans)[last_index] = *tr_ca; } else { assert(tr_ca != nullptr); *monoTrans *= *tr_ca; } } } void SymRS::encodeInitStates(void) { if (usingContextAutomaton()) { VERB("Encoding initial states (using context automaton)"); encodeInitStatesForCtxAut(); } else { FERROR("Context automaton required"); } VERB("Initial states encoded"); } void SymRS::encodeInitStatesForCtxAut(void) { initStates = new BDD(BDD_TRUE); for (unsigned int i = 0; i < totalRctSysStateVars; ++i) { *initStates *= !(*pv)[i]; } *initStates *= getEncCtxAutInitState(); } BDD SymRS::encActStrEntity(std::string proc_name, std::string entity_name) const { auto enc_entity = encCtxEntity(rs->getProcessID(proc_name), rs->getEntityID(entity_name)); return enc_entity; } size_t SymRS::getCtxAutStateEncodingSize(void) { if (!usingContextAutomaton()) { return 0; } assert(rs->ctx_aut != nullptr); size_t bitCount = 0; size_t bitCountMaxVal = 1; size_t numStates = rs->ctx_aut->statesCount(); while (bitCountMaxVal < numStates) { bitCount++; bitCountMaxVal *= 2; } VERB_LN(3, "Bits required for CA: " << bitCount); return bitCount; } BDD SymRS::encCtxAutState_raw(State state_id, bool succ) const { // select appropriate BDD vector BDDvec *enc_vec; if (succ) { enc_vec = pv_ca_succ; } else { enc_vec = pv_ca; } assert(enc_vec != nullptr); BDD r = BDD_TRUE; State val = state_id; for (unsigned int i = 0; i < totalCtxAutStateVars; ++i) { if (val != 0) { if (val % 2 == 1) { r *= (*enc_vec)[i]; } else { r *= !(*enc_vec)[i]; } val /= 2; } else { r *= !(*enc_vec)[i]; } } return r; } BDD SymRS::getEncCtxAutInitState(void) { VERB_LN(2, "Encoding context automaton's initial state"); State state = rs->ctx_aut->getInitState(); return encCtxAutState(state); } void SymRS::encodeCtxAutTrans(void) { VERB_LN(2, "Encoding context automaton's transition relation"); if (tr_ca != nullptr) { VERB_LN(1, "Encoding for context automaton already present, not replacing") return; } tr_ca = new BDD(BDD_FALSE); for (auto &t : rs->ctx_aut->transitions) { VERB_LN(2, "Encoding CA transition " << rs->ctx_aut->getStateName(t.src_state) << " -> " << rs->ctx_aut->getStateName(t.dst_state)); BDD enc_src = encCtxAutState(t.src_state); BDD enc_dst = encCtxAutStateSucc(t.dst_state); BDD enc_ctx = compContext(encContext(t.ctx)); BDD enc_drs_state = BDD_TRUE; if (t.state_constr != nullptr) { enc_drs_state = t.state_constr->getBDDforState(this); } BDD new_trans = enc_src * enc_drs_state * enc_ctx * enc_dst; *tr_ca += new_trans; } } void SymRS::reorder(void) { if (opts->reorder_trans) { VERB_L2("Reordering START"); // Cudd_ReduceHeap(cuddMgr->getManager(), CUDD_REORDER_SIFT, 10000); cuddMgr->ReduceHeap(CUDD_REORDER_GROUP_SIFT); VERB_L2("Reordering DONE"); } } /** EOF **/