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");
    }
Example #2
0
/**
 * @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";
        }
    }
Example #8
0
#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();
}
Example #11
0
// 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) << "";
    }
  }
}
Example #12
0
/**
 * 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 &current = 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);
}
Example #13
0
/**
 * 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();
    }