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"); }
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; } }
/** * @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"; } }
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(); }
/** * 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); }