diff --git a/src/storm/modelchecker/helper/ltl/SparseLTLHelper.cpp b/src/storm/modelchecker/helper/ltl/SparseLTLHelper.cpp index 57efb19c3..a6502927b 100644 --- a/src/storm/modelchecker/helper/ltl/SparseLTLHelper.cpp +++ b/src/storm/modelchecker/helper/ltl/SparseLTLHelper.cpp @@ -31,9 +31,18 @@ namespace storm { template storm::storage::Scheduler SparseLTLHelper::SparseLTLHelper::extractScheduler(storm::models::sparse::Model const& model) { STORM_LOG_ASSERT(this->isProduceSchedulerSet(), "Trying to get the produced optimal choices although no scheduler was requested."); - STORM_LOG_ASSERT(this->_productChoices.is_initialized(), "Trying to extract the produced scheduler but none is available. Was there a computation call before?"); + // If Pmax(phi) = 0 or Pmin(phi) = 1, we return a memoryless scheduler with arbitrary choices + if (_randomScheduler) { + storm::storage::Scheduler scheduler(this->_transitionMatrix.getRowGroupCount()); + for (storm::storage::sparse::state_type state = 0; state < this->_transitionMatrix.getRowGroupCount(); ++state) { + scheduler.setChoice(0, state); + + } + return scheduler; + } - // TODO fails for No accepting states, skipping probability computation. + // Otherwise, we compute a scheduler with memory. + STORM_LOG_ASSERT(this->_productChoices.is_initialized(), "Trying to extract the produced scheduler but none is available. Was there a computation call before?"); STORM_LOG_ASSERT(this->_memoryTransitions.is_initialized(), "Trying to extract the DA transition structure but none is available. Was there a computation call before?"); STORM_LOG_ASSERT(this->_memoryInitialStates.is_initialized(), "Trying to extract the initial states of the DA but there are none available. Was there a computation call before?"); @@ -50,7 +59,7 @@ namespace storm { } // initialMemoryStates: Assign an initial memory state to each initial state of the model. - for (uint_fast64_t s0 : model.getInitialStates()) { // TODO set all from _memoryInitialStates, not only mdoelInitial + for (uint_fast64_t s0 : model.getInitialStates()) { // TODO set all from _memoryInitialStates, not only modelInitial memoryBuilder.setInitialMemoryState(s0, this->_memoryInitialStates.get()[s0]); } @@ -67,7 +76,7 @@ namespace storm { storm::storage::sparse::state_type automatonState = std::get<1>(choice.first); uint_fast64_t infSet = std::get<2>(choice.first); - scheduler.setChoice(choice.second, modelState, automatonState*(_infSets.get().size()+1)+ infSet); + scheduler.setChoice(choice.second, modelState, (automatonState*(_infSets.get().size()+1))+ infSet); } // TODO // set non-reachable (modelState,memoryState)-Pairs (i.e. those that are not contained in _productChoices) to "unreachable", (extend Scheduler by something like std::vector> reachableSchedulerChoices; und isChoiceReachable(..)) @@ -245,30 +254,15 @@ namespace storm { if (infSet.size() > 0) { auto it = std::find(_infSets.get().begin(), _infSets.get().end(), infSet); if (it == _infSets.get().end()) { + infSetIds.insert(_infSets.get().size()); _infSets.get().emplace_back(infSet); - infSetIds.insert(_infSets.get().size() - 1); } else { // save ID for accCond of the MEC states - infSetIds.insert(distance(_infSets.get().begin(), it)); //TODO test + infSetIds.insert(distance(_infSets.get().begin(), it)); } } } - /* should not occur - if (infSetIds.empty()) { - - auto it = std::find(_infSets.get().begin(), _infSets.get().end(), mecStates); - if(it == _infSets.get().end()) { - _infSets.get().emplace_back(mecStates); - infSetIds.insert(_infSets.get().size()-1); - } else { - // save ID for accCond of the MEC states - infSetIds.insert(distance(_infSets.get().begin(), it)); //TODO test - } - - } - */ - // Save the InfSets into the _accInfSets for states in this MEC, but only if there weren't assigned to any other MEC yet. storm::storage::BitVector newMecStates(product->getProductModel().getNumberOfStates(), false); for (auto const &stateChoicePair : mec) { @@ -288,23 +282,24 @@ namespace storm { storm::storage::Scheduler mecScheduler(transitionMatrix.getRowGroupCount()); // States not in InfSet: Compute a scheduler that, with prob=1, reaches the infSet via mecStates starting from states that are not yet in other MEC - storm::utility::graph::computeSchedulerProb1E(newMecStates, transitionMatrix, backwardTransitions, mecStates, _infSets.get()[id], mecScheduler); + storm::utility::graph::computeSchedulerProb1E(newMecStates, transitionMatrix, backwardTransitions, mecStates, _infSets.get()[id] & mecStates, mecScheduler); //todo &mecstates? nec.? // States that already reached the InfSet: Prob1E sets an arbitrary choice for the psi states, but we want to stay in this accepting mec. + for (auto pState : (newMecStates & _infSets.get()[id])) { // If we are in the InfSet, we stay in mec-states - // TODO is this correct: - // (*std::next(mec.getChoicesForState(pState).begin(), 0)) - mecScheduler.setChoice(storm::storage::SchedulerChoice((*std::next(mec.getChoicesForState(pState).begin()), 0)), pState); + // TODO these are global? + storm::storage::FlatSet test = mec.getChoicesForState(pState); + mecScheduler.setChoice(0, pState); //todo get first choice from mec.getChoicesForState(pState); but need local + } // Extract scheduler choices (only for states that are already assigned a scheduler, i.e are in another MEC) for (auto pState : newMecStates) { // We want to reach the InfSet, save choice: ---> choice - this->_productChoices.get().insert({std::make_tuple(product->getModelState(pState),product->getAutomatonState(pState), id), mecScheduler.getChoice(pState)}); + this->_productChoices.get().insert({std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), id), mecScheduler.getChoice(pState)}); } - } } } @@ -361,7 +356,7 @@ namespace storm { } - STORM_LOG_INFO("Building "+ (Nondeterministic ? std::string("MDP-DA") : std::string("DTMC-DA")) +"product with deterministic automaton, starting from " << statesOfInterest.getNumberOfSetBits() << " model states..."); + STORM_LOG_INFO("Building "+ (Nondeterministic ? std::string("MDP-DA") : std::string("DTMC-DA")) +" product with deterministic automaton, starting from " << statesOfInterest.getNumberOfSetBits() << " model states..."); transformer::DAProductBuilder productBuilder(da, statesForAP); typename transformer::DAProduct::ptr product = productBuilder.build(this->_transitionMatrix, statesOfInterest); @@ -397,6 +392,7 @@ namespace storm { if (acceptingStates.empty()) { STORM_LOG_INFO("No accepting states, skipping probability computation."); std::vector numericResult(this->_numberOfStates, storm::utility::zero()); + this->_randomScheduler = true; return numericResult; } @@ -434,15 +430,14 @@ namespace storm { // TODO asserts _mecStatesInfSets initialized etc. // Extract the choices of the REACH-scheduler (choices to reach an acc. MEC) for the MDP-DA product: -> choice - for (storm::storage::sparse::state_type pState = 0; - pState < product->getProductModel().getNumberOfStates(); ++pState) { + for (storm::storage::sparse::state_type pState : ~acceptingStates) { // for state not in any accEC ---> choice - if(! acceptingStates.get(pState)){ - // Do not overwrite choices of states in an accepting MEC - this->_productChoices.get().insert({std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), _infSets.get().size()), prodCheckResult.scheduler->getChoice(pState)}); - // For non-accepting states that are not in any accepting EC we use the 'last' copy of the DA - this->_accInfSets.get()[pState] = {this->_infSets.get().size()}; - } + + // Do not overwrite choices of states in an accepting MEC + this->_productChoices.get().insert({std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), _infSets.get().size()), prodCheckResult.scheduler->getChoice(pState)}); + // For non-accepting states that are not in any accepting EC we use the 'last' copy of the DA + this->_accInfSets.get()[pState] = {this->_infSets.get().size()}; + } // Prepare the memory structure. For that, we need: transitions, initialMemoryStates (and memoryStateLabeling) @@ -462,23 +457,31 @@ namespace storm { if(!product->isValidProductState(modelState, automatonTo)) { // Memory state successor of the modelState-transition emanating not defined/reachable. // TODO save as unreachable in scheduler - // STORM_PRINT("set to unreachable : (" << modelState <<" , " << automatonTo <<")"); + //STORM_PRINT("set to unreachable : <" << modelState <<" , " << automatonTo <<">"); } else if (automatonTo == productBuilder.getSuccessor(modelState, automatonFrom, modelState)) { //TODO remove first parameter of getSuccessor // Add the modelState to one outgoing transition of all states of the form (Inf=lenInfSet equals not in MEC) + // For non-accepting states that are not in any accepting EC we use the 'last' copy of the DA + // For the accepting states we jump through copies of the DA wrt. the infinity sets. for (uint_fast64_t infSet = 0; infSet < _infSets.get().size()+1; ++infSet) { + // Check if we need to switch the acceptance condition if (_accInfSets.get()[product->getProductStateIndex(modelState, automatonTo)].get().count(infSet) == 0) { // the state is is in a different accepting MEC with a different accepting conjunction of InfSets. auto newInfSet = _accInfSets.get()[product->getProductStateIndex(modelState, automatonTo)].get().begin(); - // Add modelState to the transition from > to . - _memoryTransitions.get()[(automatonFrom * (_infSets.get().size()+1)) + infSet][(automatonTo * (_infSets.get().size()+1)) + *newInfSet].set(modelState); + + // todo Never switch from NoMc to Mec? + //if (*newInfSet != _infSets.get().size() ){ + // Add modelState to the transition from > to . + _memoryTransitions.get()[(automatonFrom * (_infSets.get().size()+1)) + infSet][(automatonTo * (_infSets.get().size()+1)) + *newInfSet].set(modelState); + // } + } else { // The state is either not in an accepting EC or in an accepting EC that needs to satisfy the infSet. - if (infSet == _infSets.get().size() || _infSets.get()[infSet].get(product->getProductStateIndex(modelState, automatonTo))) { + if (infSet == _infSets.get().size() || !(_infSets.get()[infSet].get(product->getProductStateIndex(modelState, automatonTo)))) { // is not in any accepting EC or does not satisfy the InfSet, we stay there. // Add modelState to the transition from to _memoryTransitions.get()[(automatonFrom * (_infSets.get().size()+1)) + infSet][(automatonTo * (_infSets.get().size()+1)) + infSet].set(modelState); diff --git a/src/storm/modelchecker/helper/ltl/SparseLTLHelper.h b/src/storm/modelchecker/helper/ltl/SparseLTLHelper.h index d73fe39b4..d60027ade 100644 --- a/src/storm/modelchecker/helper/ltl/SparseLTLHelper.h +++ b/src/storm/modelchecker/helper/ltl/SparseLTLHelper.h @@ -96,11 +96,12 @@ namespace storm { storm::storage::SparseMatrix const& _transitionMatrix; std::size_t _numberOfStates; //TODO just use _transitionMatrix.getRowGroupCount instead? - // REACH scheduler and MEC scheduler + // scheduler + bool _randomScheduler = false; boost::optional, storm::storage::SchedulerChoice>> _productChoices; // ---> ReachChoice and ---> MecChoice + boost::optional> _infSets; // Save the InfSets of the Acceptance condition. boost::optional>>> _accInfSets; // Save for each product state (which is assigned to an acceptingMEC), the infSets that need to be visited inf often to satisfy the acceptance condition. Remaining states belonging to no accepting EC, are assigned len(_infSets) (REACH scheduler) - // Memory structure boost::optional>> _memoryTransitions; // The BitVector contains the model states that lead from startState to . This is deterministic, because each state is assigned to a unique MEC (scheduler). boost::optional> _memoryInitialStates; // Save for each relevant state (initial or all) s its unique initial memory state (which memory state is reached from the initial state after reading s)