void addAutomaticallyGeneratedLivenessAssumption() { // Create a new liveness assumption that says that always eventually, if an action/pre-state // combination may lead to a different position, then it is taken // (the completion variables always eventually catch up to the activation) BF prePostMotionStatesDifferent = mgr.constantFalse(); for (uint i=0;i<preMotionStateVars.size();i++) { prePostMotionStatesDifferent |= (preMotionStateVars[i] ^ postMotionStateVars[i]); } BF preMotionInputCombinationsThatCanChangeState = (prePostMotionStatesDifferent & robotBDD).ExistAbstract(varCubePostMotionState); BF newLivenessAssumption = (!preMotionInputCombinationsThatCanChangeState) | prePostMotionStatesDifferent; // before adding the assumption, check first that it is not somehow already satisfied. bool doesNewLivenessExist = false; for (unsigned int i=0;i<livenessAssumptions.size();i++) { if ((livenessAssumptions[i] & (!newLivenessAssumption)).isFalse()) { doesNewLivenessExist = true; break; } } if (!doesNewLivenessExist) { livenessAssumptions.push_back(newLivenessAssumption); if (!(newLivenessAssumption.isTrue())) { std::cerr << "Note: Added a liveness assumption that always eventually, we are moving if an action is taken that allows moving.\n"; } BF_newDumpDot(*this,newLivenessAssumption,"PreMotionState PreMotionControlOutput PostMotionState","/tmp/changeMotionStateLivenessAssumption.dot"); } // Make sure that there is at least one liveness assumption and one liveness guarantee // The synthesis algorithm might be unsound otherwise if (livenessGuarantees.size()==0) livenessGuarantees.push_back(mgr.constantTrue()); if (livenessAssumptions.size()==0) livenessAssumptions.push_back(mgr.constantTrue()); BF_newDumpDot(*this,robotBDD,"PreMotionState PreMotionControlOutput PostMotionState","/tmp/robotBDD.dot"); }
/** * @brief A function that takes a BF "in" over the set of variables "var" and returns a new BF over the same variables * that only represents one concrete variable valuation in "in" to the variables in "var" * @param in a BF to determinize * @param vars the care set of variables * @return the determinized BF */ BF GR1Context::determinize(BF in, std::vector<BF> vars) { for (auto it = vars.begin(); it!=vars.end(); it++) { BF res = in & !(*it); if (res.isFalse()) { in = in & *it; } else { in = res; } } return in; }
void checkRealizability() { computeWinningPositions(); // Check if for every possible environment initial position the system has a good system initial position BF result; if (specialRoboticsSemantics) { result = (initEnv & initSys & winningPositions).ExistAbstract(varCubePreOutput).ExistAbstract(varCubePreInput); } else { result = (initEnv & (initSys.Implies(winningPositions)).UnivAbstract(varCubePreOutput)).ExistAbstract(varCubePreInput); } // Check if the result is well-defind. Might fail after an incorrect modification of the above algorithm if (!result.isConstant()) throw "Internal error: Could not establish realizability/unrealizability of the specification."; // Return the result in Boolean form. realizable = !result.isTrue(); }
void addDeadlocked(BF targetPositionCandidateSet, std::pair<size_t, std::pair<unsigned int, unsigned int> > current, std::vector<BF> &bfsUsedInTheLookupTable, std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > &lookupTableForPastStates, std::ostream &outputStream) { BF newCombination = determinize(targetPositionCandidateSet, postVars) ; newCombination = newCombination.ExistAbstract(varCubePreOutput).SwapVariables(varVectorPre,varVectorPost); std::pair<size_t, std::pair<unsigned int, unsigned int> > target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(current.second.first, current.second.second)); unsigned int tn; if (lookupTableForPastStates.count(target)==0) { tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size(); bfsUsedInTheLookupTable.push_back(newCombination); outputStream << tn << "\n"; //Note that since we are printing here, we usually end up with the states being printed out of order. //TOTO: print in order outputStream << "State " << tn << " with rank (" << current.second.first << "," << current.second.second << ") -> <"; bool first = true; for (unsigned int i=0;i<variables.size();i++) { if (doesVariableInheritType(i,PreInput)) { if (first) { first = false; } else { outputStream << ", "; } outputStream << variableNames[i] << ":"; outputStream << (((newCombination & variables[i]).isFalse())?"0":"1"); } } outputStream << ">\n\tWith no successors."; } else { tn = lookupTableForPastStates[target]; outputStream << tn; } }
void addAutomaticallyGeneratedSafeties() { // Create new safety assumptions describing which regions the robot can end up in given the currently active controllers // and new safety guarantees describing which controllers can be activated BF newSafetyEnv = robotBDD; BF newSafetySys = robotBDD.ExistAbstract(varCubePostMotionState); safetyEnv &= newSafetyEnv; safetySys &= newSafetySys; std::cerr << "Note: Added a safety assumption and guarantee describing the robot's allowed transitions.\n"; BF_newDumpDot(*this,newSafetyEnv,"PreMotionState PreMotionControlOutput PostMotionState","/tmp/changeMotionStateSafetyAssumption.dot"); }
/** * @brief Modified basic synthesis algorithm - adds to 'strategyDumpingData' transitions * that represent "a bias for action" */ void computeWinningPositions() { // Compute system transitions that are not environment dead-ends. // The system is only allowed to use these. BF nonDeadEndSafetySys = safetySys & safetyEnv.ExistAbstract(varCubePost).SwapVariables(varVectorPre,varVectorPost); // BF_newDumpDot(*this,nonDeadEndSafetySys,"Pre Post","/tmp/nonDeadEndSys.dot"); // BF_newDumpDot(*this,safetyEnv.ExistAbstract(varCubePost).SwapVariables(varVectorPre,varVectorPost),"Pre Post","/tmp/notDeadEndSafetyEnv.dot"); // Copy all liveness assumptions into liveness guarantees - we are // them seperately at the end of a "guarantee cycle". for (auto it = livenessAssumptions.begin();it!=livenessAssumptions.end();it++) { livenessGuarantees.push_back(*it); } std::vector<BF> transitionsTowardsLivenessAssumption(livenessAssumptions.size()); // The greatest fixed point - called "Z" in the GR(1) synthesis paper BFFixedPoint nu2(mgr.constantTrue()); unsigned int nu2Nr = 0; // Iterate until we have found a fixed point for (;!nu2.isFixedPointReached();) { nu2Nr++; // To extract a strategy in case of realizability, we need to store a sequence of 'preferred' transitions in the // game structure. These preferred transitions only need to be computed during the last execution of the outermost // greatest fixed point. Since we don't know which one is the last one, we store them in every iteration, // so that after the last iteration, we obtained the necessary data. Before any new iteration, we need to // clear the old data, though. strategyDumpingData.clear(); // Iterate over all of the liveness guarantees. Put the results into the variable 'nextContraintsForGoals' for every // goal. Then, after we have iterated over the goals, we can update nu2. BF nextContraintsForGoals = mgr.constantTrue(); for (unsigned int j=0;j<livenessGuarantees.size()-livenessAssumptions.size();j++) { // Start computing the transitions that lead closer to the goal and lead to a position that is not yet known to be losing. // Start with the ones that actually represent reaching the goal (which is a transition in this implementation as we can have // nexts in the goal descriptions). BF livetransitions = livenessGuarantees[j] & (nu2.getValue().SwapVariables(varVectorPre,varVectorPost)); // Compute the middle least-fixed point (called 'Y' in the GR(1) paper) BFFixedPoint mu1(mgr.constantFalse()); unsigned int mu1Nr = 0; for (;!mu1.isFixedPointReached();) { mu1Nr++; // Update the set of transitions that lead closer to the goal. livetransitions |= mu1.getValue().SwapVariables(varVectorPre,varVectorPost); // { std::ostringstream os; // os << "/tmp/livetransitions" << nu2Nr << "-" << j << "-" << mu1Nr << ".dot"; // BF_newDumpDot(*this,livetransitions,"Pre Post",os.str()); } // Iterate over the liveness assumptions. Store the positions that are found to be winning for *any* // of them into the variable 'goodForAnyLivenessAssumption'. BF goodForAnyLivenessAssumption = mu1.getValue(); for (unsigned int i=0;i<livenessAssumptions.size();i++) { // Prepare the variable 'foundPaths' that contains the transitions that stay within the inner-most // greatest fixed point or get closer to the goal. Only used for strategy extraction BF foundPaths = mgr.constantTrue(); // Allocate space for storing the transitions that allow the satisfaction of // the liveness assumptions std::vector<BF> livenessAssumptionProgressPaths; // Inner-most greatest fixed point. The corresponding variable in the paper would be 'X'. BFFixedPoint nu0(mgr.constantTrue()); unsigned int nu0Nr = 0; for (;!nu0.isFixedPointReached();) { nu0Nr++; // Cooperation: Compute set of states from which a live transition can be // reached (somehow) BFFixedPoint muCoop(mgr.constantFalse()); unsigned int muCoopNr = 0; livenessAssumptionProgressPaths.clear(); livenessAssumptionProgressPaths.push_back(livetransitions); for (;!muCoop.isFixedPointReached();) { muCoopNr++; foundPaths = livetransitions | ((nu0.getValue() & muCoop.getValue()).SwapVariables(varVectorPre,varVectorPost) & !(livenessAssumptions[i])); foundPaths &= nonDeadEndSafetySys & safetyEnv; // { std::ostringstream os; // os << "/tmp/foundPaths" << nu2Nr << "-" << j << "-" << mu1Nr << "-" << i << "-" << nu0Nr << "--" << muCoopNr << ".dot"; // BF_newDumpDot(*this,foundPaths,"Pre Post",os.str()); } livenessAssumptionProgressPaths.push_back(foundPaths); muCoop.update((safetyEnv & foundPaths).ExistAbstract(varCubePost)); // { std::ostringstream os; // os << "/tmp/muCoop" << nu2Nr << "-" << j << "-" << mu1Nr << "-" << i << "-" << nu0Nr << "--" << muCoopNr << ".dot"; // BF_newDumpDot(*this,muCoop.getValue(),"Pre Post",os.str()); } } // { std::ostringstream os; // os << "/tmp/foundPathsFinal" << nu2Nr << "-" << j << "-" << mu1Nr << "-" << i << ".dot"; // BF_newDumpDot(*this,foundPaths,"Pre Post",os.str()); } // Update the inner-most fixed point with the result of applying the enforcable predecessor operator nu0.update(safetyEnv.Implies(foundPaths).ExistAbstract(varCubePostOutput).UnivAbstract(varCubePostInput)); // { std::ostringstream os; // os << "/tmp/nu0-" << nu2Nr << "-" << j << "-" << mu1Nr << "-" << i << "-" << nu0Nr << ".dot"; // BF_newDumpDot(*this,nu0.getValue(),"Pre Post",os.str()); } } // Update the set of positions that are winning for some liveness assumption goodForAnyLivenessAssumption |= nu0.getValue(); // Dump the paths that we just wound into 'strategyDumpingData' - store the current goal long // with the BDD for (auto it = livenessAssumptionProgressPaths.begin();it!=livenessAssumptionProgressPaths.end();it++) { strategyDumpingData.push_back(std::pair<unsigned int,BF>(j,foundPaths & *it)); } } // Update the moddle fixed point mu1.update(goodForAnyLivenessAssumption); // { std::ostringstream os; // os << "/tmp/mu1-" << nu2Nr << "-" << j << "-" << mu1Nr << ".dot"; // BF_newDumpDot(*this,mu1.getValue(),"Pre Post",os.str()); } } // Update the set of positions that are winning for any goal for the outermost fixed point nextContraintsForGoals &= mu1.getValue(); } // Now iterate over the liveness assumptions // and compute a sub-strategy to ensure that there is always some way in which the // liveness assumptions can be satisfied for (unsigned int j=livenessGuarantees.size()-livenessAssumptions.size();j<livenessGuarantees.size();j++) { BF currentLivenessAssumption = livenessGuarantees[j]; // Collect a set of paths to satisfy this liveness assumption, while the system can // always enforce to land in a winning state afterwards BFFixedPoint mu1(mgr.constantFalse()); unsigned int mu1Nr = 0; BF goodTransitions = currentLivenessAssumption & nonDeadEndSafetySys & nextContraintsForGoals & safetyEnv & nextContraintsForGoals.SwapVariables(varVectorPre,varVectorPost); for (;!mu1.isFixedPointReached();) { mu1Nr++; // { std::ostringstream os; // os << "/tmp/fairtransitions-" << nu2Nr << "-" << j << "-" << mu1Nr << ".dot"; // BF_newDumpDot(*this,goodTransitions,"Pre Post",os.str()); } // { std::ostringstream os; // os << "/tmp/fairtransitionCL-" << nu2Nr << "-" << j << "-" << mu1Nr << ".dot"; // BF_newDumpDot(*this,currentLivenessAssumption,"Pre Post",os.str()); } // { std::ostringstream os; // os << "/tmp/fairmu-" << nu2Nr << "-" << j << "-" << mu1Nr << ".dot"; // BF_newDumpDot(*this,mu1.getValue(),"Pre Post",os.str()); } // Dump good transitions strategyDumpingData.push_back(std::pair<unsigned int,BF>(j,goodTransitions)); // From which states can we enforce that the environment can enforce a // goodTransition? We cannot offer losing transitions. BF winningStates = goodTransitions.ExistAbstract(varCubePost); goodTransitions |= winningStates.SwapVariables(varVectorPre,varVectorPost) & safetyEnv & nonDeadEndSafetySys & nextContraintsForGoals; mu1.update(winningStates | mu1.getValue()); } nextContraintsForGoals &= mu1.getValue(); transitionsTowardsLivenessAssumption[j-(livenessGuarantees.size()-livenessAssumptions.size())] = goodTransitions; // Backup transitions not towards the goal strategyDumpingData.push_back(std::pair<unsigned int,BF>(j,nonDeadEndSafetySys & nu2.getValue().SwapVariables(varVectorPre,varVectorPost))); } // Update the outer-most fixed point nu2.update(nextContraintsForGoals); // { std::ostringstream os; // os << "/tmp/nu2-" << nu2Nr << ".dot"; // BF_newDumpDot(*this,nu2.getValue(),"Pre Post",os.str()); } } // Modify the additional liveness guarantees to be satisfied once the environment deviated // from the paths towards the environment liveness goal. for (unsigned int j=livenessGuarantees.size()-livenessAssumptions.size();j<livenessGuarantees.size();j++) { livenessGuarantees[j] |= !transitionsTowardsLivenessAssumption[j-(livenessGuarantees.size()-livenessAssumptions.size())]; } // We found the set of winning positions winningPositions = nu2.getValue(); }
/** * @brief Compute and print out (to stdout) an explicit-state strategy that is winning for * the system. The output is compatible with the old JTLV output of LTLMoP. * This function requires that the realizability of the specification has already been * detected and that the variables "strategyDumpingData" and * "winningPositions" have been filled by the synthesis algorithm with meaningful data. * @param outputStream - Where the strategy shall be printed to. */ void computeAndPrintExplicitStateStrategy(std::ostream &outputStream) { // We don't want any reordering from this point onwards, as // the BDD manipulations from this point onwards are 'kind of simple'. mgr.setAutomaticOptimisation(false); // List of states in existance so far. The first map // maps from a BF node pointer (for the pre variable valuation) and a goal // to a state number. The vector then contains the concrete valuation. std::map<std::pair<size_t, unsigned int>, unsigned int > lookupTableForPastStates; std::vector<BF> bfsUsedInTheLookupTable; std::list<std::pair<size_t, unsigned int> > todoList; // Prepare initial to-do list from the allowed initial states BF todoInit = (oneStepRecovery)?(winningPositions & initSys):(winningPositions & initSys & initEnv); while (!(todoInit.isFalse())) { BF concreteState = determinize(todoInit,preVars); std::pair<size_t, unsigned int> lookup = std::pair<size_t, unsigned int>(concreteState.getHashCode(),0); lookupTableForPastStates[lookup] = bfsUsedInTheLookupTable.size(); bfsUsedInTheLookupTable.push_back(concreteState); todoInit &= !concreteState; todoList.push_back(lookup); } // Prepare positional strategies for the individual goals std::vector<BF> positionalStrategiesForTheIndividualGoals(livenessGuarantees.size()); for (unsigned int i=0;i<livenessGuarantees.size();i++) { BF casesCovered = mgr.constantFalse(); BF strategy = mgr.constantFalse(); for (auto it = strategyDumpingData.begin();it!=strategyDumpingData.end();it++) { if (it->first == i) { BF newCases = it->second.ExistAbstract(varCubePostOutput) & !casesCovered; strategy |= newCases & it->second; casesCovered |= newCases; } } positionalStrategiesForTheIndividualGoals[i] = strategy; //BF_newDumpDot(*this,strategy,"PreInput PreOutput PostInput PostOutput","/tmp/generalStrategy.dot"); } // Extract strategy while (todoList.size()>0) { std::pair<size_t, unsigned int> current = todoList.front(); todoList.pop_front(); unsigned int stateNum = lookupTableForPastStates[current]; BF currentPossibilities = bfsUsedInTheLookupTable[stateNum]; /*{ std::ostringstream filename; filename << "/tmp/state" << stateNum << ".dot"; BF_newDumpDot(*this,currentPossibilities,"PreInput PreOutput PostInput PostOutput",filename.str()); }*/ // Print state information outputStream << "State " << stateNum << " with rank " << current.second << " -> <"; bool first = true; for (unsigned int i=0;i<variables.size();i++) { if (variableTypes[i] < PostInput) { if (first) { first = false; } else { outputStream << ", "; } outputStream << variableNames[i] << ":"; outputStream << (((currentPossibilities & variables[i]).isFalse())?"0":"1"); } } outputStream << ">\n\tWith successors : "; first = true; // Compute successors for all variables that allow these currentPossibilities &= positionalStrategiesForTheIndividualGoals[current.second]; BF remainingTransitions = (oneStepRecovery)? currentPossibilities: (currentPossibilities & safetyEnv); // Switching goals while (!(remainingTransitions.isFalse())) { BF newCombination = determinize(remainingTransitions,postVars); // Jump as much forward in the liveness guarantee list as possible ("stuttering avoidance") unsigned int nextLivenessGuarantee = current.second; bool firstTry = true; while (((nextLivenessGuarantee != current.second) || firstTry) && !((livenessGuarantees[nextLivenessGuarantee] & newCombination).isFalse())) { nextLivenessGuarantee = (nextLivenessGuarantee + 1) % livenessGuarantees.size(); firstTry = false; } // Mark which input has been captured by this case BF inputCaptured = newCombination.ExistAbstract(varCubePostOutput); newCombination = newCombination.ExistAbstract(varCubePre).SwapVariables(varVectorPre,varVectorPost); remainingTransitions &= !inputCaptured; // Search for newCombination unsigned int tn; std::pair<size_t, unsigned int> target = std::pair<size_t, unsigned int>(newCombination.getHashCode(),nextLivenessGuarantee); if (lookupTableForPastStates.count(target)==0) { tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size(); bfsUsedInTheLookupTable.push_back(newCombination); todoList.push_back(target); } else { tn = lookupTableForPastStates[target]; } // Print if (first) { first = false; } else { outputStream << ", "; } outputStream << tn; } outputStream << "\n"; } }
#include <string> #include <cstdint> #include <functional> #include "catch.hpp" #include "bf.h" TEST_CASE("Edge cases", "[bf][edge-case]") { BF bf; bytestream is, os; SECTION("pointer underflow") { std::string src("<"); REQUIRE(bf.interpret(src, is, os)); REQUIRE(bf.ptr() == BF::MEM_SIZE - 1); } SECTION("pointer overflow") { std::string src(BF::MEM_SIZE, '>'); REQUIRE(bf.interpret(src, is, os)); REQUIRE(bf.ptr() == 0); } SECTION("memory byte underflow") { std::string src("-."); REQUIRE(bf.interpret(src, is, os)); uint8_t v;
void computeAndPrintExplicitStateStrategy(std::ostream &outputStream) { // We don't want any reordering from this point onwards, as // the BDD manipulations from this point onwards are 'kind of simple'. mgr.setAutomaticOptimisation(false); // List of states in existance so far. The first map // maps from a BF node pointer (for the pre variable valuation) and a goal // to a state number. The vector then contains the concrete valuation. std::map<std::pair<size_t, std::pair<unsigned int, unsigned int> >, unsigned int > lookupTableForPastStates; std::vector<BF> bfsUsedInTheLookupTable; std::list<std::pair<size_t, std::pair<unsigned int, unsigned int> > > todoList; // Prepare positional strategies for the individual goals std::vector<std::vector<BF> > positionalStrategiesForTheIndividualGoals(livenessAssumptions.size()); for (unsigned int i=0;i<livenessAssumptions.size();i++) { //BF casesCovered = mgr.constantFalse(); std::vector<BF> strategy(livenessGuarantees.size()+1); for (unsigned int j=0;j<livenessGuarantees.size()+1;j++) { strategy[j] = mgr.constantFalse(); } for (auto it = strategyDumpingData.begin();it!=strategyDumpingData.end();it++) { if (boost::get<0>(*it) == i) { //Have to cover each guarantee (since the winning strategy depends on which guarantee is being pursued) //Essentially, the choice of which guarantee to pursue can be thought of as a system "move". //The environment always to chooses that prevent the appropriate guarantee. strategy[boost::get<1>(*it)] |= boost::get<2>(*it).UnivAbstract(varCubePostOutput) & !(strategy[boost::get<1>(*it)].ExistAbstract(varCubePost)); } } positionalStrategiesForTheIndividualGoals[i] = strategy; } // Prepare initial to-do list from the allowed initial states. Select a single initial input valuation. // TODO: Support for non-special-robotics semantics BF todoInit = (winningPositions & initEnv & initSys); while (!(todoInit.isFalse())) { BF concreteState = determinize(todoInit,preVars); //find which liveness guarantee is being prevented (finds the first liveness in order specified) // Note by Ruediger here: Removed "!livenessGuarantees[j]" as condition as it is non-positional unsigned int found_j_index = 0; for (unsigned int j=0;j<livenessGuarantees.size();j++) { if (!(concreteState & positionalStrategiesForTheIndividualGoals[0][j]).isFalse()) { found_j_index = j; break; } } std::pair<size_t, std::pair<unsigned int, unsigned int> > lookup = std::pair<size_t, std::pair<unsigned int, unsigned int> >(concreteState.getHashCode(),std::pair<unsigned int, unsigned int>(0,found_j_index)); lookupTableForPastStates[lookup] = bfsUsedInTheLookupTable.size(); bfsUsedInTheLookupTable.push_back(concreteState); //from now on use the same initial input valuation (but consider all other initial output valuations) todoInit &= !concreteState; todoList.push_back(lookup); } // Extract strategy while (todoList.size()>0) { std::pair<size_t, std::pair<unsigned int, unsigned int> > current = todoList.front(); todoList.pop_front(); unsigned int stateNum = lookupTableForPastStates[current]; BF currentPossibilities = bfsUsedInTheLookupTable[stateNum]; // Print state information outputStream << "State " << stateNum << " with rank (" << current.second.first << "," << current.second.second << ") -> <"; bool first = true; for (unsigned int i=0;i<variables.size();i++) { if (doesVariableInheritType(i,Pre)) { if (first) { first = false; } else { outputStream << ", "; } outputStream << variableNames[i] << ":"; outputStream << (((currentPossibilities & variables[i]).isFalse())?"0":"1"); } } outputStream << ">\n\tWith successors : "; first = true; // Can we enforce a deadlock? BF deadlockInput = (currentPossibilities & safetyEnv & !safetySys).UnivAbstract(varCubePostOutput); if (deadlockInput!=mgr.constantFalse()) { addDeadlocked(deadlockInput, current, bfsUsedInTheLookupTable, lookupTableForPastStates, outputStream); } else { // No deadlock in sight -> Jump to a different liveness guarantee if necessary. while ((currentPossibilities & positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second])==mgr.constantFalse()) current.second.second = (current.second.second + 1) % livenessGuarantees.size(); currentPossibilities &= positionalStrategiesForTheIndividualGoals[current.second.first][current.second.second]; assert(currentPossibilities != mgr.constantFalse()); BF remainingTransitions = currentPossibilities; // save any combination of pre variables and post inputs found that are not included in player 1's strategy BF_newDumpDot(*this,remainingTransitions,NULL,"/tmp/remainingTransitions.dot"); BF foundCutConditions = (!remainingTransitions.ExistAbstract(varCubePre)) & remainingTransitions.ExistAbstract(varCubePost); candidateFailingPreConditions |= remainingTransitions; BF_newDumpDot(*this,!(remainingTransitions).ExistAbstract(varCubePre),NULL,"/tmp/candidateWinningThisState.dot"); std::stringstream ss1; std::stringstream ss2; ss1 << "/tmp/candidateWinning" << stateNum << ".dot"; ss2 << "/tmp/remainingTransitions" << stateNum << ".dot"; BF_newDumpDot(*this,(!remainingTransitions.ExistAbstract(varCubePre)) & remainingTransitions.ExistAbstract(varCubePost),NULL,ss1.str()); BF_newDumpDot(*this,remainingTransitions,NULL,ss2.str()); // Choose one next input and stick to it! // BF_newDumpDot(*this,remainingTransitions,NULL,"/tmp/remainingTransitionsBefore.dot"); remainingTransitions = determinize(remainingTransitions,postInputVars); // BF_newDumpDot(*this,remainingTransitions,NULL,"/tmp/remainingTransitionsAfter.dot"); // Switching goals while (!(remainingTransitions & safetySys).isFalse()) { BF safeTransition = remainingTransitions & safetySys; BF newCombination = determinize(safeTransition, postOutputVars); foundCutPostConditions |= foundCutConditions & newCombination.ExistAbstract(varCubePre).ExistAbstract(varCubePostInput); // Jump as much forward in the liveness assumption list as possible ("stuttering avoidance") unsigned int nextLivenessAssumption = current.second.first; bool firstTry = true; while (((nextLivenessAssumption != current.second.first) | firstTry) && !((livenessAssumptions[nextLivenessAssumption] & newCombination).isFalse())) { nextLivenessAssumption = (nextLivenessAssumption + 1) % livenessAssumptions.size(); firstTry = false; } //Mark which input has been captured by this case. Use the same input for other successors remainingTransitions &= !newCombination; // We don't need the pre information from the point onwards anymore. newCombination = newCombination.ExistAbstract(varCubePre).SwapVariables(varVectorPre,varVectorPost); unsigned int tn; std::pair<size_t, std::pair<unsigned int, unsigned int> > target; target = std::pair<size_t, std::pair<unsigned int, unsigned int> >(newCombination.getHashCode(),std::pair<unsigned int, unsigned int>(nextLivenessAssumption, current.second.second)); if (lookupTableForPastStates.count(target)==0) { tn = lookupTableForPastStates[target] = bfsUsedInTheLookupTable.size(); bfsUsedInTheLookupTable.push_back(newCombination); todoList.push_back(target); } else { tn = lookupTableForPastStates[target]; } // Print if (first) { first = false; } else { outputStream << ", "; } outputStream << tn; } } outputStream << "\n"; } }
void computeWinningPositions() { // The greatest fixed point - called "Z" in the GR(1) synthesis paper BFFixedPoint mu2(mgr.constantFalse()); // Iterate until we have found a fixed point for (;!mu2.isFixedPointReached();) { // Iterate over all of the liveness guarantees. Put the results into the variable 'nextContraintsForGoals' for every // goal. Then, after we have iterated over the goals, we can update mu2. BF nextContraintsForGoals = mgr.constantFalse(); for (unsigned int j=0;j<livenessGuarantees.size();j++) { // To extract a counterstrategy in case of unrealizability, we need to store a sequence of 'preferred' transitions in the // game structure. These preferred transitions only need to be computed during the last execution of the middle // greatest fixed point. Since we don't know which one is the last one, we store them in every iteration, // so that after the last iteration, we obtained the necessary data. Before any new iteration, we need to // store the old date so that it can be discarded if needed std::vector<boost::tuple<unsigned int, unsigned int,BF> > strategyDumpingDataOld = strategyDumpingData; // Start computing the transitions that lead closer to the goal and lead to a position that is not yet known to be losing (for the environment). // Start with the ones that actually represent reaching the goal (which is a transition in this implementation as we can have // nexts in the goal descriptions). BF livetransitions = (!livenessGuarantees[j]) | (mu2.getValue().SwapVariables(varVectorPre,varVectorPost)); // Compute the middle least-fixed point (called 'Y' in the GR(1) paper) BFFixedPoint nu1(mgr.constantTrue()); for (;!nu1.isFixedPointReached();) { // New middle iteration has begun -> revert to the data before the first iteration. strategyDumpingData = strategyDumpingDataOld; // Update the set of transitions that lead closer to the goal. livetransitions &= nu1.getValue().SwapVariables(varVectorPre,varVectorPost); // Iterate over the liveness assumptions. Store the positions that are found to be winning for *all* // of them into the variable 'goodForAnyLivenessAssumption'. BF goodForAllLivenessAssumptions = nu1.getValue(); for (unsigned int i=0;i<livenessAssumptions.size();i++) { // Prepare the variable 'foundPaths' that contains the transitions that stay within the inner-most // greatest fixed point or get closer to the goal. Only used for counterstrategy extraction BF foundPaths = mgr.constantFalse(); // Inner-most greatest fixed point. The corresponding variable in the paper would be 'X'. BFFixedPoint mu0(mgr.constantFalse()); for (;!mu0.isFixedPointReached();) { // Compute a set of paths that are safe to take - used for the enforceable predecessor operator ('cox') foundPaths = livetransitions & (mu0.getValue().SwapVariables(varVectorPre,varVectorPost) | (livenessAssumptions[i])); foundPaths = (safetyEnv & safetySys.Implies(foundPaths)).UnivAbstract(varCubePostOutput); // Dump the paths that we just found into 'strategyDumpingData' - store the current goal // with the BDD strategyDumpingData.push_back(boost::make_tuple(i,j,foundPaths)); // Update the inner-most fixed point with the result of applying the enforcable predecessor operator mu0.update(foundPaths.ExistAbstract(varCubePostInput)); } // Update the set of positions that are winning for some liveness assumption goodForAllLivenessAssumptions &= mu0.getValue(); } // Update the middle fixed point nu1.update(goodForAllLivenessAssumptions); } // Update the set of positions that are winning for some environment goal for the outermost fixed point nextContraintsForGoals |= nu1.getValue(); } // Update the outer-most fixed point mu2.update(nextContraintsForGoals); } // We found the set of winning positions winningPositions = mu2.getValue(); }
// Print out a contracted gaussian basis function, in the form: // #Prims = ..., s/px/py/pz/dxy/... type orbital // Coefficients: ...... // Exponents: ...... void Logger::print(BF& bf) const { // Get information Vector coef; coef = bf.getCoeffs(); Vector exp; exp = bf.getExps(); int lx = bf.getLx(); int ly = bf.getLy(); int lz = bf.getLz(); // Work out the orbital type std::string temp; switch(lx + ly + lz){ case 0: { temp = "s"; break; } case 1: { if (lx == 1){ temp = "px"; } else if (ly == 1){ temp = "py"; } else { temp = "pz"; } break; } case 2: { if (lx == 1 && ly == 1){ temp = "dxy"; } else if (lx == 1 && lz == 1){ temp = "dxz"; } else if (ly == 1 && lz == 1){ temp = "dyz"; } else if (lz == 2) { temp = "dz2"; } else { temp = "d(x2 - y2)"; } break; } case 3: { temp = "f"; break; } case 4: { temp = "g"; break; } case 5: { temp = "h"; break; } case 6: { temp = "Very angular"; break; } } // Print it out outfile << "#Primitives = " << coef.size(); outfile << ", " << temp << " type basis function\n"; outfile << std::setw(15) << "Coefficients: "; for (int i = 0; i < coef.size(); i++){ outfile << std::setw(12) << std::setprecision(9) << coef(i); if ((i % 4) == 0 && i != 0){ // Print 4 per line outfile << "\n" << std::setw(15) << ""; } } outfile << "\n"; outfile << std::setw(15) << "Exponents: "; for (int i = 0; i < exp.size(); i++){ outfile << std::setw(12) << std::setprecision(9) << exp(i); if ( (i%4) == 0 && i!=0) { outfile << "\n" << std::setw(15) << ""; } } }
/** * Internal function for BDD dumping */ std::pair<int,unsigned int> BDD_newDumpDotRecurse(const BF &bdd, int level, RecurseContext &rc) { const BFManager *cudd = bdd.manager(); if (rc.nodesSoFar>MAX_NODES_GRAPH) return std::pair<int,unsigned int>(-1,0); // Constant nodes? if (bdd==cudd->constantFalse()) return std::pair<int,unsigned int>(-1,0); if (bdd==cudd->constantTrue()) return std::pair<int,unsigned int>(-1,1); // int index = bdd.NodeReadIndex(); if (level==(int)(rc.vars.size())) { #ifdef USE_BDD int index = bdd.readNodeIndex(); std::ostringstream errorMsg; errorMsg << "MyDumpDot: Variable List was incomplete. Variable missing (" << index << "): " << rc.io.getVariableName(index); std::cerr << errorMsg.str() << std::endl; throw BFDumpDotException(errorMsg.str()); #else throw BFDumpDotException("MyDumpDot: Variable List was incomplete. Variable missing (unknown);"); #endif } // While not dependent on this var skip to next one BF currentVar; BF zeroBranch; BF oneBranch; bool depends = false; while ((!depends) && (level<(int)(rc.vars.size()))) { currentVar = rc.io.getVariableBF(rc.vars[level]); BF cube[1]; int phase[1]; phase[0] = 1; cube[0] = currentVar; zeroBranch = bdd & (!currentVar); zeroBranch = zeroBranch.ExistAbstract( cudd->computeCube(cube,phase,1)); oneBranch = bdd & (currentVar); oneBranch = oneBranch.ExistAbstract( cudd->computeCube(cube,phase,1)); if (oneBranch==zeroBranch) { level++; } else { depends = true; } } if (level==(int)(rc.vars.size())) { #ifdef USE_BDD int index = bdd.readNodeIndex(); std::ostringstream errorMsg; errorMsg << "MyDumpDot: Variable List was incomplete. Variable missing (" << index << "): " << rc.io.getVariableName(index); std::cerr << errorMsg.str() << std::endl; throw BFDumpDotException(errorMsg.str()); #else throw BFDumpDotException("MyDumpDot: Variable List was incomplete. Variable missing (unknown)"); #endif } // Search for a suitable node for (unsigned int i=0;i<rc.nodes[level].size();i++) { MyDumpDotNode ¤t = rc.nodes[level][i]; if ((current.getSuccF()==zeroBranch) && (current.getSuccT()==oneBranch)) return std::pair<int,unsigned int>(level,i); } // Else: Make suitable node std::pair<int,unsigned int> continueZero = BDD_newDumpDotRecurse(zeroBranch, level+1, rc); std::pair<int,unsigned int> continueOne = BDD_newDumpDotRecurse(oneBranch, level+1, rc); rc.nodes[level].push_back(MyDumpDotNode(zeroBranch,oneBranch,continueZero.first,continueZero.second,continueOne.first,continueOne.second)); rc.nodesSoFar++; return std::pair<int,unsigned int>(level,rc.nodes[level].size()-1); }
/** * Dumps a BDD to a file for rendering with DOT * * Uses a custom variable ordering and "manual" dumping without "negation" edges */ void BF_newDumpDot(const VariableInfoContainer &cont, const BF &b, const char* varOrder, const std::string filename) { // Use NULL as varOrder to just use everything in the list std::string allVars; if (varOrder==NULL) { std::ostringstream os; std::vector<string> types; cont.getVariableTypes(types); for (std::vector<string>::const_iterator it = types.begin();it!=types.end();it++) { if (os.str().length()>0) os << " "; os << *it; } allVars = os.str(); varOrder = allVars.c_str(); } // Decoding Variable order std::istringstream order(varOrder); std::vector<unsigned int> vars; while (!order.eof()) { std::string part; order >> part; std::vector<unsigned int> newVars; cont.getVariableNumbersOfType(part,newVars); for (unsigned int i=0;i<newVars.size();i++) { std::vector<unsigned int>::iterator first_previous_usage; first_previous_usage = std::find(vars.begin(),vars.end(),newVars[i]); if (first_previous_usage != vars.end()) { // A variable occurs twice! throw BFDumpDotException(__FILE__,__LINE__); } vars.push_back(newVars[i]); //std::cout << "bddDump Variable: " << newVars[i] << std::endl; } } if (order.fail()) throw BFDumpDotException("Illegal variable order."); // Process the nodes // This is done by pushing a node downwards whenever it is independent of the current variable. std::vector<std::vector< MyDumpDotNode > > nodes; for (unsigned int i=0;i<vars.size();i++) nodes.push_back(std::vector<MyDumpDotNode>()); RecurseContext context(vars,nodes,cont); // Process recursively BDD_newDumpDotRecurse(b,0,context); // Opening output file std::ofstream dumpFile(filename.c_str()); if (context.nodesSoFar<=MAX_NODES_GRAPH) { // Preface dumpFile << "digraph \"DD\" { size = \"8,8\" \n center = true; " << std::endl; // Start with variable order dumpFile << "edge [dir = none];\n" << "{ node [shape = plaintext];\n" << " edge [style = invis];\n" << " \"CONST NODES\" [style = invis];\n"; std::vector<string> localVarNames; for (unsigned int i=0;i<vars.size();i++) { if (nodes[i].size()>0) { unsigned int var = vars[i]; std::ostringstream thisOne; std::string varName = cont.getVariableName(var); std::replace(varName.begin(), varName.end(), '\"', '\''); thisOne << "\"" << varName << "-"; thisOne << " (" << var << ")\""; localVarNames.push_back(thisOne.str()); dumpFile << thisOne.str(); dumpFile << " -> "; } else { localVarNames.push_back("--INVALID--"); } } dumpFile << "\"CONST NODES\"\n}\n"; // Print the normal nodes for (unsigned int i=0;i<vars.size();i++) { if (nodes[i].size()>0) { dumpFile << "{ rank = same; " << localVarNames[i] << "; "; std::ostringstream thisOne; for (unsigned int j=0;j<nodes[i].size();j++) { thisOne << " \"" << i << "_" << j << "\"; "; } dumpFile << thisOne.str() << "\n}\n"; } } // Print the const nodes dumpFile << "{ rank = same; \"CONST NODES\";" << std::endl; { BFManager const *cudd = b.manager(); if (b==cudd->constantFalse()) dumpFile << "{ node [shape = box]; \"0\"; }}"; else dumpFile << "{ node [shape = box]; \"1\"; }}"; } // Print the Connections for (unsigned int i=0;i<vars.size();i++) { if (nodes[i].size()>0) { for (unsigned int j=0;j<nodes[i].size();j++) { std::ostringstream thisOne; // ELSE (But no zero-Transitions) if ((nodes[i][j].getSuccFlevel()!=-1) || (nodes[i][j].getSuccFnr()!=0)) { thisOne << " \"" << i << "_" << j << "\" ->"; if (nodes[i][j].getSuccFlevel()==-1) { if (nodes[i][j].getSuccFnr()==0) thisOne << "\"0\""; else thisOne << "\"1\""; } else { thisOne << " \"" << nodes[i][j].getSuccFlevel() << "_" << nodes[i][j].getSuccFnr() << "\""; } thisOne << "[color=red];\n"; } // IF if ((nodes[i][j].getSuccTlevel()!=-1) || (nodes[i][j].getSuccTnr()!=0)) { thisOne << " \"" << i << "_" << j << "\" ->"; if (nodes[i][j].getSuccTlevel()==-1) { if (nodes[i][j].getSuccTnr()==0) thisOne << "\"0\""; else thisOne << "\"1\""; } else { thisOne << " \"" << nodes[i][j].getSuccTlevel() << "_" << nodes[i][j].getSuccTnr() << "\""; } thisOne << "[color=blue,style=bold,dir=forward];\n"; } dumpFile << thisOne.str(); } } } dumpFile << "\n}\n"; } else { dumpFile << "digraph \"DD\" { \n \"Graph is too big\"\n}\n"; } dumpFile.close(); if (dumpFile.fail()) throw BFDumpDotException("MyDotDump: Writing to output file "+filename+" failed."); }
void checkRealizability() { // The greatest fixed point - called "Z" in the GR(1) synthesis paper BFFixedPoint nu2(mgr.constantTrue()); // Iterate until we have found a fixed point for (;!nu2.isFixedPointReached();) { // To extract a strategy in case of realizability, we need to store a sequence of 'preferred' transitions in the // game structure. These preferred transitions only need to be computed during the last execution of the outermost // greatest fixed point. Since we don't know which one is the last one, we store them in every iteration, // so that after the last iteration, we obtained the necessary data. Before any new iteration, we need to // clear the old data, though. strategyDumpingData.clear(); // Iterate over all of the liveness guarantees. Put the results into the variable 'nextContraintsForGoals' for every // goal. Then, after we have iterated over the goals, we can update nu2. BF nextContraintsForGoals = mgr.constantTrue(); for (uint j=0;j<livenessGuarantees.size();j++) { // Start computing the transitions that lead closer to the goal and lead to a position that is not yet known to be losing. // Start with the ones that actually represent reaching the goal (which is a transition in this implementation as we can have // nexts in the goal descriptions). BF livetransitions = livenessGuarantees[j] & (nu2.getValue().SwapVariables(varVectorPre,varVectorPost)); //BF_newDumpDot(*this,livetransitions,NULL,"/tmp/liveTransitions.dot"); // Compute the middle least-fixed point (called 'Y' in the GR(1) paper) BFFixedPoint mu1(mgr.constantFalse()); for (;!mu1.isFixedPointReached();) { // Update the set of transitions that lead closer to the goal. livetransitions |= mu1.getValue().SwapVariables(varVectorPre,varVectorPost); // Iterate over the liveness assumptions. Store the positions that are found to be winning for *any* // of them into the variable 'goodForAnyLivenessAssumption'. BF goodForAnyLivenessAssumption = mu1.getValue(); for (uint i=0;i<livenessAssumptions.size();i++) { // Prepare the variable 'foundPaths' that contains the transitions that stay within the inner-most // greatest fixed point or get closer to the goal. Only used for strategy extraction BF foundPaths = mgr.constantTrue(); // Inner-most greatest fixed point. The corresponding variable in the paper would be 'X'. BFFixedPoint nu0(mgr.constantTrue()); for (;!nu0.isFixedPointReached();) { // Compute a set of paths that are safe to take - used for the enforceable predecessor operator ('cox') foundPaths = livetransitions | (nu0.getValue().SwapVariables(varVectorPre,varVectorPost) & !(livenessAssumptions[i])); foundPaths &= safetySys; // Update the inner-most fixed point with the result of applying the enforcable predecessor operator nu0.update(safetyEnv.Implies(foundPaths).ExistAbstract(varCubePostControllerOutput).UnivAbstract(varCubePostInput)); } // Update the set of positions that are winning for some liveness assumption goodForAnyLivenessAssumption |= nu0.getValue(); // Dump the paths that we just wound into 'strategyDumpingData' - store the current goal long // with the BDD strategyDumpingData.push_back(std::pair<uint,BF>(j,foundPaths)); } // Update the moddle fixed point mu1.update(goodForAnyLivenessAssumption); } // Update the set of positions that are winning for any goal for the outermost fixed point nextContraintsForGoals &= mu1.getValue(); } // Update the outer-most fixed point nu2.update(nextContraintsForGoals); } // We found the set of winning positions winningPositions = nu2.getValue(); BF_newDumpDot(*this,winningPositions,NULL,"/tmp/winningPositions.dot"); // Check if for every possible environment initial position the system has a good system initial position BF result; if (initSpecialRoboticsSemantics) { // TODO: Support for special-robotics semantics throw SlugsException(false,"Error: special robot init semantics not yet supported.\n"); } else { result = initEnv.Implies(winningPositions & initSys).ExistAbstract(varCubePreMotionState).ExistAbstract(varCubePreControllerOutput).UnivAbstract(varCubePreInput); } // Check if the result is well-defind. Might fail after an incorrect modification of the above algorithm if (!result.isConstant()) throw "Internal error: Could not establish realizability/unrealizability of the specification."; // Return the result in Boolean form. realizable = result.isTrue(); }