diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2015-01-19 16:41:18 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2015-01-19 16:41:18 (GMT) |
commit | ff86d690dc02d7dd495000331d378e7d8eb688ac (patch) | |
tree | 5214786f7e575952d3cba0919e5071f3a783050b /src/uscxml/transform/ChartToFSM.cpp | |
parent | 42437db418574f2a80d098e568b9498a21343800 (diff) | |
download | uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.zip uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.gz uscxml-ff86d690dc02d7dd495000331d378e7d8eb688ac.tar.bz2 |
Plenty of smaller fixes and adaptations
Diffstat (limited to 'src/uscxml/transform/ChartToFSM.cpp')
-rw-r--r-- | src/uscxml/transform/ChartToFSM.cpp | 798 |
1 files changed, 734 insertions, 64 deletions
diff --git a/src/uscxml/transform/ChartToFSM.cpp b/src/uscxml/transform/ChartToFSM.cpp index 8597211..38262db 100644 --- a/src/uscxml/transform/ChartToFSM.cpp +++ b/src/uscxml/transform/ChartToFSM.cpp @@ -36,21 +36,22 @@ #define UNDECIDABLE 2147483647 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -#define DUMP_STATS(nrTrans) \ +#define DUMP_STATS(nrTrans, disregardTime) \ uint64_t now = tthread::chrono::system_clock::now(); \ -if (now - _lastTimeStamp > 1000) { \ +if (now - _lastTimeStamp > 1000 || disregardTime) { \ std::cerr << "## Transition: " << _perfTransUsed << " / " << _perfTransTotal << " [" << _perfTransProcessed << "/sec]"; \ if (nrTrans > 0) { \ std::cerr << " - 2**" << nrTrans << " = " << pow(2.0, static_cast<double>(nrTrans)); \ } \ std::cerr << std::endl; \ - std::cerr << "## State : " << _globalConf.size() << " [" << _perfStatesProcessed << "/sec]" << std::endl; \ + std::cerr << "## State : " << _globalConf.size() << " found / " << _perfStackSize << " stacked / " << _perfStatesTotal << " seen [" << _perfStatesProcessed << "/sec]" << std::endl; \ std::cerr << "## Microstep : " << _perfMicroStepTotal << " [" << _perfMicroStepProcessed << "/sec]" << std::endl; \ std::cerr << "## Cached : " << _perfStatesCachedTotal << " [" << _perfStatesCachedProcessed << "/sec]" << std::endl; \ std::cerr << "## Skipped : " << _perfStatesSkippedTotal << " [" << _perfStatesSkippedProcessed << "/sec]" << std::endl; \ std::cerr << "## Queues : " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ + std::cerr << "toFSM: "; \ std::cerr << _perfTransUsed << ", " << _perfTransTotal << ", " << _perfTransProcessed << ", "; \ - std::cerr << _globalConf.size() << ", " << _perfStatesProcessed << ", "; \ + std::cerr << _globalConf.size() << ", " << _perfStackSize << ", " << _perfStatesTotal << ", " << _perfStatesProcessed << ", "; \ std::cerr << _perfMicroStepTotal << ", " << _perfMicroStepProcessed << ", "; \ std::cerr << _perfStatesCachedTotal << ", " << _perfStatesCachedProcessed << ", " << _perfStatesSkippedTotal << ", " << _perfStatesSkippedProcessed << ", "; \ std::cerr << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << ", " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; \ @@ -60,7 +61,8 @@ if (now - _lastTimeStamp > 1000) { \ _perfStatesCachedProcessed = 0; \ _perfStatesSkippedProcessed = 0; \ _perfMicroStepProcessed = 0; \ - _lastTimeStamp = now; \ + if (!disregardTime)\ + _lastTimeStamp = now; \ } //std::cerr << "Q: " << (_maxEventRaisedChain == UNDECIDABLE ? "UNK" : toStr(_maxEventRaisedChain)) << " / " << (_maxEventSentChain == UNDECIDABLE ? "UNK" : toStr(_maxEventSentChain)) << std::endl; @@ -89,6 +91,83 @@ for (int i = 0; i < contents.size(); i++) { \ } \ std::cerr << ")"; +std::list<std::set<Element<std::string> > > Complexity::getAllConfigurations(const Arabica::DOM::Element<std::string>& root) { + + std::list<std::set<Element<std::string> > > allConfigurations; + std::string nsPrefix = (root.getPrefix().size() > 0 ? root.getPrefix() + ":" : ""); + std::string localName = root.getLocalName(); + bool isAtomic = true; + + NodeList<std::string> children = root.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE) + continue; + Element<std::string> childElem(children.item(i)); + if (childElem.getTagName() == nsPrefix + "state" || + childElem.getTagName() == nsPrefix + "parallel" || + childElem.getTagName() == nsPrefix + "final") { + // nested child state + std::list<std::set<Element<std::string> > > nestedConfigurations = getAllConfigurations(childElem); + isAtomic = false; + if (localName == "parallel" && allConfigurations.size() > 0) { + // for every nested configuration, every new nested is valid + std::list<std::set<Element<std::string> > > combinedConfigurations; + for (std::list<std::set<Element<std::string> > >::iterator existIter = allConfigurations.begin(); existIter != allConfigurations.end(); existIter++) { + std::set<Element<std::string> > existingConfig = *existIter; + + for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + + std::set<Element<std::string> > newConfig = *newIter; + std::set<Element<std::string> > combinedSet; + combinedSet.insert(existingConfig.begin(), existingConfig.end()); + combinedSet.insert(newConfig.begin(), newConfig.end()); + + combinedConfigurations.push_back(combinedSet); + } + } + allConfigurations = combinedConfigurations; + } else { + // just add nested configurations and this + for (std::list<std::set<Element<std::string> > >::iterator newIter = nestedConfigurations.begin(); newIter != nestedConfigurations.end(); newIter++) { + std::set<Element<std::string> > newConfig = *newIter; + if (localName != "scxml") + newConfig.insert(root); + allConfigurations.push_back(newConfig); + } + } + } + } + + if (isAtomic) { + std::set<Element<std::string> > soleConfig; + soleConfig.insert(root); + allConfigurations.push_back(soleConfig); + } + return allConfigurations; +} + +std::map<size_t, size_t> Complexity::getTransitionHistogramm(const Arabica::DOM::Element<std::string>& root) { + std::map<size_t, size_t> histogram; + std::string nameSpace; + + std::list<std::set<Element<std::string> > > allConfig = Complexity::getAllConfigurations(root); + + // for every legal configuration, count the transitions + for (std::list<std::set<Element<std::string> > >::iterator confIter = allConfig.begin(); confIter != allConfig.end(); confIter++) { + NodeSet<std::string> configNodeSet; + std::set<Element<std::string> > config = *confIter; + for (std::set<Element<std::string> >::iterator elemIter = config.begin(); elemIter != config.end(); elemIter++) { + configNodeSet.push_back(*elemIter); + if (nameSpace.size() == 0 && elemIter->getPrefix().size() > 0) + nameSpace = elemIter->getPrefix() + ":"; + } + NodeSet<std::string> transitions = InterpreterImpl::filterChildElements(nameSpace + "transition", configNodeSet); + histogram[transitions.size()]++; + } + + return histogram; +} + uint64_t Complexity::stateMachineComplexity(const Arabica::DOM::Element<std::string>& root, Variant variant) { Complexity complexity = calculateStateMachineComplexity(root); @@ -183,11 +262,15 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { cloneFrom(other.getImpl()); + _transitionsFromTree = true; + _keepInvalidTransitions = false; _lastTimeStamp = tthread::chrono::system_clock::now(); _perfTransProcessed = 0; _perfTransTotal = 0; _perfTransUsed = 0; + _perfStatesTotal = 0; _perfStatesProcessed = 0; + _perfStackSize = 0; _perfStatesSkippedProcessed = 0; _perfStatesSkippedTotal = 0; _perfStatesCachedProcessed = 0; @@ -195,9 +278,13 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { _perfMicroStepProcessed = 0; _perfMicroStepTotal = 0; + if (envVarIEquals("USCXML_TRANSFORM_TRANS_FROM", "powerset")) + _transitionsFromTree = false; + _start = NULL; _currGlobalTransition = NULL; - + _transTree = NULL; + _lastStateIndex = 0; _lastActiveIndex = 0; _lastTransIndex = 0; @@ -207,10 +294,6 @@ ChartToFSM::ChartToFSM(const Interpreter& other) { _doneEventRaiseTolerance = 0; _skipEventChainCalculations = false; - // create a _flatDoc for the FSM - DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); - _flatDoc = domFactory.createDocument(other.getDocument().getNamespaceURI(), "", 0); - addMonitor(this); } @@ -235,14 +318,38 @@ ChartToFSM::~ChartToFSM() { } Document<std::string> ChartToFSM::getDocument() const { - return _flatDoc; + if (_flatDoc) + return _flatDoc; + return _document; } InterpreterState ChartToFSM::interpret() { + // create a _flatDoc for the FSM + DOMImplementation<std::string> domFactory = Arabica::SimpleDOM::DOMImplementation<std::string>::getDOMImplementation(); + _flatDoc = domFactory.createDocument(_document.getNamespaceURI(), "", 0); + init(); setupIOProcessors(); + { + std::list<std::set<Element<std::string> > > allConfig = Complexity::getAllConfigurations(_scxml); + for (std::list<std::set<Element<std::string> > >::iterator confIter = allConfig.begin(); confIter != allConfig.end(); confIter++) { + std::string seperator; + NodeSet<std::string> configNodeSet; + std::set<Element<std::string> > config = *confIter; + for (std::set<Element<std::string> >::iterator elemIter = config.begin(); elemIter != config.end(); elemIter++) { +// std::cerr << seperator << ATTR((*elemIter), "id"); + seperator = ","; + configNodeSet.push_back(*elemIter); + } + assert(isLegalConfiguration(configNodeSet)); +// std::cerr << std::endl; + } + } + std::map<size_t, size_t> histoGramm = Complexity::getTransitionHistogramm(_scxml); +// abort(); + uint64_t complexity = Complexity::stateMachineComplexity(_scxml) + 1; std::cerr << "Approximate Complexity: " << complexity << std::endl; std::cerr << "Approximate Active Complexity: " << Complexity::stateMachineComplexity(_scxml, Complexity::IGNORE_HISTORY_AND_NESTED_DATA) + 1 << std::endl; @@ -288,7 +395,7 @@ InterpreterState ChartToFSM::interpret() { } _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - _alreadyFlat = (HAS_ATTR(_scxml, "flat") && DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))); + _alreadyFlat = (HAS_ATTR(_scxml, "flat") && stringIsTrue(ATTR(_scxml, "flat"))); if (_alreadyFlat) { reassembleFromFlat(); @@ -333,10 +440,7 @@ InterpreterState ChartToFSM::interpret() { // std::cout << _scxml << std::endl; - indexTransitions(_scxml); - - // reverse indices for most prior to be in front - std::reverse(indexedTransitions.begin(), indexedTransitions.end()); + indexTransitions(); // add initial transitions as least prior for (int i = 0; i < initialTransitions.size() ; i++) { @@ -345,6 +449,8 @@ InterpreterState ChartToFSM::interpret() { // set index attribute for transitions for (int i = 0; i < indexedTransitions.size(); i++) { +// std::cerr << toStr(i) << ":" << (HAS_ATTR(indexedTransitions[i], "line_start") ? ATTR(indexedTransitions[i], "line_start") : ""); +// std::cerr << "\t" << DOMUtils::xPathForNode(indexedTransitions[i]) << std::endl; indexedTransitions[i].setAttribute("index", toStr(i)); } @@ -385,6 +491,8 @@ InterpreterState ChartToFSM::interpret() { explode(); + DUMP_STATS(0, true); + #if 0 // print set of global configurations for(std::map<std::string, GlobalState*>::iterator globalConfIter = _globalConf.begin(); @@ -400,8 +508,8 @@ InterpreterState ChartToFSM::interpret() { std::cerr << "Internal Queue: " << _maxEventRaisedChain << std::endl; std::cerr << "External Queue: " << _maxEventSentChain << std::endl; - if (complexity < _globalConf.size()) - throw std::runtime_error("Upper bound for states exceeded"); +// if (complexity < _globalConf.size()) +// throw std::runtime_error("Upper bound for states exceeded"); return _state; } @@ -476,6 +584,7 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat if (parentIsScxmlState(state)) return; +// return; // std::cerr << "internalDoneSend: " << state << std::endl; // create onentry with a raise element @@ -505,7 +614,7 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat raise.setAttribute("event", "done.state." + ATTR_CAST(state.getParentNode(), "id")); // parent?! GlobalTransition::Action action; - action.onEntry = onentry; + action.raiseDone = onentry; // HERE! _currGlobalTransition->actions.push_back(action); if (!_skipEventChainCalculations && @@ -517,10 +626,10 @@ void ChartToFSM::internalDoneSend(const Arabica::DOM::Element<std::string>& stat static bool isSuperset(const GlobalTransition* t1, const GlobalTransition* t2) { bool isSuperset = true; - + if (t1->transitionRefs.size() >= t2->transitionRefs.size()) return false; - + NodeSet<std::string> t1Trans = t1->getTransitions(); NodeSet<std::string> t2Trans = t2->getTransitions(); @@ -551,6 +660,25 @@ bool ChartToFSM::filterSameState(const NodeSet<std::string>& transitions) { return true; } +static bool filterSameHierarchy(const NodeSet<std::string>& transitions) { + for (unsigned int i = 0; i < transitions.size(); i++) { + Node<std::string> t1 = transitions[i]; + Node<std::string> p1 = InterpreterImpl::getParentState(t1); + for (unsigned int j = i + 1; j < transitions.size(); j++) { + Node<std::string> t2 = transitions[j]; + Node<std::string> p2 = InterpreterImpl::getParentState(t2); + while(p2) { + if (p1 == p2) { + return false; + } + p2 = p2.getParentNode(); + } + } + } + return true; +} + + static bool filterChildEnabled(const NodeSet<std::string>& transitions) { // drop any transition that is already enabled by a child NodeSet<std::string> filteredTransitions; @@ -634,6 +762,19 @@ void ChartToFSM::annotateRaiseAndSend(const Arabica::DOM::Element<std::string>& } } +void ChartToFSM::indexTransitions() { + indexTransitions(_scxml); + + size_t index = 0; + for (std::vector<Arabica::DOM::Element<std::string> >::iterator transIter = indexedTransitions.begin(); transIter != indexedTransitions.end(); transIter++) { + transIter->setAttribute("priority", toStr(index)); + index++; + } + + // reverse indices for most prior to be in front + std::reverse(indexedTransitions.begin(), indexedTransitions.end()); +} + void ChartToFSM::indexTransitions(const Arabica::DOM::Element<std::string>& root) { // breadth first traversal of transitions Arabica::XPath::NodeSet<std::string> levelTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", root); @@ -676,7 +817,7 @@ template <typename T> bool PtrComp(const T * const & a, const T * const & b) { /** * subset only removes transitions without cond -> superset will always be enabled */ -bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second) { +bool hasUnconditionalSuperset(GlobalTransition* first, GlobalTransition* second) { NodeSet<std::string> firstTransitions = first->getTransitions(); NodeSet<std::string> secondTransitions = first->getTransitions(); @@ -694,6 +835,9 @@ bool hasUnconditionalSuperset (GlobalTransition* first, GlobalTransition* second return false; //second can't be removed } +/** + * earlier transition is conditionless for same event + */ bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* second) { if (first->eventDesc == second->eventDesc) { if (first->condition.size() == 0) @@ -702,9 +846,43 @@ bool hasEarlierUnconditionalMatch(GlobalTransition* first, GlobalTransition* sec return false; } -// for some reason, unique is not quite up to the task -std::list<GlobalTransition*> reapplyUniquePredicates(std::list<GlobalTransition*> list) { +std::list<GlobalTransition*> redundantRemove(std::list<GlobalTransition*> list) { +#if 1 + std::list<GlobalTransition*>::iterator outerIter; + std::list<GlobalTransition*>::iterator innerIter; + + outerIter = list.begin(); + while(outerIter != list.end()) { + innerIter = outerIter; + + while(innerIter != list.end()) { + if (innerIter == outerIter) { + innerIter++; + continue; + } + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (hasUnconditionalSuperset(t1, t2)) { + list.erase(innerIter++); + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + list.erase(outerIter++); + break; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + list.erase(innerIter++); + continue; + } + innerIter++; + } + + outerIter++; + } +#else + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); outerIter != list.end(); outerIter++) { @@ -719,25 +897,425 @@ std::list<GlobalTransition*> reapplyUniquePredicates(std::list<GlobalTransition* GlobalTransition* t2 = *innerIter; if (hasUnconditionalSuperset(t1, t2)) { - list.erase(outerIter++); + innerIter = list.erase(innerIter); continue; } else if (hasUnconditionalSuperset(t2, t1)) { - list.erase(innerIter++); + outerIter = list.erase(outerIter); continue; } if (hasEarlierUnconditionalMatch(t1, t2)) { - list.erase(innerIter++); + innerIter = list.erase(innerIter); continue; } } } +#endif + return list; +} + +std::list<GlobalTransition*> redundantMark(std::list<GlobalTransition*> list) { +#if 1 + std::list<GlobalTransition*>::iterator outerIter; + std::list<GlobalTransition*>::iterator innerIter; + outerIter = list.begin(); + while(outerIter != list.end()) { + innerIter = outerIter; + + while(innerIter != list.end()) { + if (innerIter == outerIter) { + innerIter++; + continue; + } + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (!t1->isValid || !t2->isValid) { + innerIter++; + continue; + } + + if (hasUnconditionalSuperset(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Unconditional superset"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + innerIter++; + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + t1->isValid = false; + t1->invalidMsg = "Unconditional superset"; + t1->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + outerIter++; + break; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Earlier unconditional match"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_MATCH; + innerIter++; + continue; + } + innerIter++; + } + + outerIter++; + } + +#else + + for (std::list<GlobalTransition*>::iterator outerIter = list.begin(); + outerIter != list.end(); + outerIter++) { + for (std::list<GlobalTransition*>::iterator innerIter = outerIter; + innerIter != list.end(); + innerIter++) { + + if (innerIter == outerIter) + continue; + + GlobalTransition* t1 = *outerIter; + GlobalTransition* t2 = *innerIter; + + if (!t1->isValid || !t2->isValid) + continue; + + if (hasUnconditionalSuperset(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Unconditional superset"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } else if (hasUnconditionalSuperset(t2, t1)) { + t1->isValid = false; + t1->invalidMsg = "Unconditional superset"; + t1->invalidReason = GlobalTransition::UNCONDITIONAL_SUPERSET; + continue; + } + if (hasEarlierUnconditionalMatch(t1, t2)) { + t2->isValid = false; + t2->invalidMsg = "Earlier unconditional match"; + t2->invalidReason = GlobalTransition::UNCONDITIONAL_MATCH; + continue; + } + } + } +#endif return list; } -void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { + +void TransitionTreeNode::dump(int indent) { + std::string padding; + for (int i = 0; i + 1 < indent; i++) { + padding += "| "; + } + if (indent > 0) + padding += "|-"; + + std::string typeString; + switch (type) { + case TYPE_NESTED: + typeString = "NESTED"; break; + case TYPE_PARALLEL: + typeString = "PARALLEL"; break; + case TYPE_TRANSITION: + typeString = "TRANSITION"; break; + case TYPE_UNDEFINED: + typeString = "UNDEFINED"; break; + break; + default: + break; + } + + + if (transition) { + std::cerr << padding << "t" << ATTR(transition, "index") << " " << typeString << ": "; +// std::cerr << (prevTransition != NULL ? " (" + prevTransition->nodeId + ") <-" : ""); + std::cerr << "[" << nodeId << "]"; +// std::cerr << (nextTransition != NULL ? " -> (" + nextTransition->nodeId + ")" : ""); + std::cerr << std::endl; + } else { + std::cerr << padding << ATTR(state, "id") << " " << typeString << ": " << "[" << nodeId << "]"; +// std::cerr << (firstTransition != NULL ? " -> " + firstTransition->nodeId : ""); + std::cerr << std::endl; + } + + for (std::list<TransitionTreeNode*>::iterator childIter = children.begin(); childIter != children.end(); childIter++) { + (*childIter)->dump(indent + 1); + } +} + +void ChartToFSM::getPotentialTransitionsForConfFromTree(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { + if (_transTree == NULL) { + _transTree = buildTransTree(_scxml, "0"); +// _transTree->dump(); + } + std::string seperator; + + +// std::cerr << "--- "; + + // recursion start + std::set<TransitionTreeNode*> transLeafs; + + for (int i = 0; i < conf.size(); i++) { + DUMP_STATS(conf.size(), false); + + Element<std::string> confElem(conf[i]); + assert(_stateToTransTreeNode.find(confElem) != _stateToTransTreeNode.end()); + TransitionTreeNode* node = _stateToTransTreeNode[confElem]; + if (node->firstState == NULL) { // a leaf - ignore intermediates + // ascend to the first parent with transitions but stop at parallel nodes + while(node != NULL && node->firstTransition == NULL) { + if (node->parent && node->parent->type == TransitionTreeNode::TYPE_PARALLEL) + break; + node = node->parent; + } + if (node != NULL) { + transLeafs.insert(node); + } else { + //std::cerr << ATTR(confElem, "id") << " does not cause transitions" << std::endl; + } + } + } + + std::list<std::set<TransitionTreeNode*> > stack; + stack.push_back(transLeafs); // push follow-up configurations onto stack + + while (stack.size() > 0) { + // pop from front of stack + std::set<TransitionTreeNode*> stateList = stack.front(); + stack.pop_front(); + + DUMP_STATS(conf.size(), false); + +#if 0 + seperator = ""; + std::cerr << "Current set: "; + for (std::set<TransitionTreeNode*>::iterator transIter = stateList.begin(); transIter != stateList.end(); transIter++) { + std::cerr << seperator << (*transIter)->nodeId; + seperator = ", "; + } + std::cerr << std::endl; +#endif + + /* + * TransNodes contains a set of lists of transitions. + * In the inner stack we build every possible combination + * of picking at-most one from each list. + */ + + /* create global transitions for every n-tuple in current set of lists */ + std::list<std::pair<std::set<TransitionTreeNode*>, std::set<TransitionTreeNode*> > > innerStack; + innerStack.push_back(std::make_pair(std::set<TransitionTreeNode*>(), stateList)); + + while(innerStack.size() > 0) { + + // picking at-most one from each list + std::set<TransitionTreeNode*> remainingStates = innerStack.front().second; + std::set<TransitionTreeNode*> fixedTransitions = innerStack.front().first; + innerStack.pop_front(); + + if (remainingStates.size() > 0) { + // iterate for each first element fixed + TransitionTreeNode* firstRemainingState = *remainingStates.begin(); + remainingStates.erase(remainingStates.begin()); + + if (firstRemainingState->firstTransition == NULL) { + // no transitions at this state - reenqueue with NULL selection from this + innerStack.push_back(std::make_pair(fixedTransitions, remainingStates)); + continue; + } + + TransitionTreeNode* currTrans = firstRemainingState->firstTransition; + + // choose none from firstList + innerStack.push_back(std::make_pair(fixedTransitions, remainingStates)); + + while(currTrans != NULL) { + std::set<TransitionTreeNode*> fixedAndThis(fixedTransitions); + fixedAndThis.insert(currTrans); + innerStack.push_back(std::make_pair(fixedAndThis, remainingStates)); + currTrans = currTrans->nextTransition; + } + } else { + DUMP_STATS(conf.size(), false); + + if (fixedTransitions.size() > 0) { + + _perfTransTotal++; + _perfTransProcessed++; + + NodeSet<std::string> fixed; + +#if 0 + seperator = ""; + for (std::set<TransitionTreeNode*>::iterator itemIter = fixedTransitions.begin(); itemIter != fixedTransitions.end(); itemIter++) { + TransitionTreeNode* currItem = *itemIter; + std::cerr << seperator << currItem->nodeId; + seperator = ", "; + } + std::cerr << " ## "; +#endif + + seperator = ""; + for (std::set<TransitionTreeNode*>::iterator itemIter = fixedTransitions.begin(); itemIter != fixedTransitions.end(); itemIter++) { + TransitionTreeNode* currItem = *itemIter; + fixed.push_back(currItem->transition); +// std::cerr << seperator << ATTR(currItem->transition, "index"); + seperator = ", "; + } +// std::cerr << std::endl; + + // fixed contains a transiton set! + assert(filterSameState(fixed)); +// assert(filterChildEnabled(fixed)); + assert(filterSameHierarchy(fixed)); + // do not add if they preempt + if (fixed.size() != removeConflictingTransitions(fixed).size()) { +// std::cerr << " - PREEMPTS" << std::endl; + continue; + } + + GlobalTransition* transition = new GlobalTransition(fixed, _dataModel, this); + transition->index = _lastTransIndex++; + +// assert(outMap.find(transition->transitionId) == outMap.end()); + + if (!transition->isValid && !_keepInvalidTransitions) { + delete(transition); +// std::cerr << " - INVALID" << std::endl; + continue; + } + + _perfTransUsed++; + + outMap[transition->transitionId] = transition; +// std::cerr << " - GOOD" << std::endl; + } + } + } + + // create new set of transition lists by moving to parent states + for (std::set<TransitionTreeNode*>::iterator stateIter = stateList.begin(); stateIter != stateList.end(); stateIter++) { + TransitionTreeNode* origState = *stateIter; + TransitionTreeNode* currState = origState; + TransitionTreeNode* parentState = currState->parent; + + /** + * We ascend the current state via its parent and add the parent with transitions. + * However, we break if we reached the top or if we passed a parallel state for + * wich we are not the first child + */ + + while(parentState != NULL) { + if (parentState->type == TransitionTreeNode::TYPE_PARALLEL && parentState->firstState != currState) { + // the first child of the parallel state will continue this transition - we made sure to keep them + break; + } + + if (parentState->firstTransition != NULL) { +// std::cerr << "#### Adding new parent lists for " << origState->nodeId << std::endl; + + std::set<TransitionTreeNode*> newStateList; + newStateList.insert(parentState); + + // add all other states that are not a child of the parent state + for (std::set<TransitionTreeNode*>::iterator newlistIter = stateList.begin(); newlistIter != stateList.end(); newlistIter++) { + TransitionTreeNode* otherState = *newlistIter; + while(otherState != NULL && otherState != parentState) { + otherState = otherState->parent; + } + if (otherState == NULL) + newStateList.insert(*newlistIter); + } + if (newStateList.size() > 0) + stack.push_back(newStateList); + break; + } + + currState = currState->parent; + parentState = currState->parent; + } + } + } +} + +TransitionTreeNode* ChartToFSM::buildTransTree(const Arabica::DOM::Element<std::string>& root, const std::string& nodeId) { + TransitionTreeNode* stateNode = new TransitionTreeNode(); + stateNode->nodeId = nodeId; + stateNode->state = root; + + if (TAGNAME(root) == _nsInfo.xmlNSPrefix + "parallel") { + stateNode->type = TransitionTreeNode::TYPE_PARALLEL; + } else { + stateNode->type = TransitionTreeNode::TYPE_NESTED; + } + + // get all transitions and states from root without recursing + NodeSet<std::string> nested; + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "transition", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "state", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "final", root)); + nested.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "parallel", root)); + nested.to_document_order(); + + TransitionTreeNode* lastNode = NULL; + + for (int i = 0; i < nested.size(); i++) { + Element<std::string> nestedElem(nested[i]); + if (TAGNAME(nestedElem) == _nsInfo.xmlNSPrefix + "transition") { + TransitionTreeNode* transNode = new TransitionTreeNode(); + transNode->transition = nestedElem; + transNode->parent = stateNode; + transNode->nodeId = nodeId + "-" + toStr(i); + transNode->type = TransitionTreeNode::TYPE_TRANSITION; + + if (stateNode->firstTransition == NULL) { + stateNode->firstTransition = transNode; + } + stateNode->children.push_back(transNode); + stateNode->lastTransition = transNode; + + if (lastNode != NULL) { + lastNode->nextTransition = transNode; + transNode->prevTransition = lastNode; + } + lastNode = transNode; + + + } else { + TransitionTreeNode* deeperNode = buildTransTree(nestedElem, nodeId + "-" + toStr(i)); + if (stateNode->firstState == NULL) { + stateNode->firstState = deeperNode; + } + + deeperNode->parent = stateNode; + stateNode->children.push_back(deeperNode); + } + } + + _stateToTransTreeNode[root] = stateNode; + + return stateNode; +} + +void ChartToFSM::getPotentialTransitionsForConfFromPowerSet(const Arabica::XPath::NodeSet<std::string>& conf, std::map<std::string, GlobalTransition*>& outMap) { // get all transition elements from states in the current configuration NodeSet<std::string> allTransitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", conf); + + { + std::string seperator = ""; + for (int i = 0; i < allTransitions.size(); i++) { + std::cerr << seperator << ATTR_CAST(allTransitions[i], "index"); + seperator=", "; + } + std::cerr << std::endl; + } + +// if (true) { +// outMap = _confToTransitions[""]; +// } if (allTransitions.size() == 0) return; // no transitions @@ -747,6 +1325,12 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st int* stack = (int*)malloc((nrElements + 1) * sizeof(int)); memset(stack, 0, (nrElements + 1) * sizeof(int)); + /** + * Powerset is too naive and takes too long! + * We have it up to 500k checks/sec and still 2**30 is + * 1G+ for 30minutes in a single state out of 50k+! + */ + while(1) { // create the power set of all potential transitions - this is expensive! // see: http://www.programminglogic.com/powerset-algorithm-in-c/ @@ -765,7 +1349,7 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st break; NodeSet<std::string> transitions; - // std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; +// std::cerr << globalState->stateId << " [" << nrElements << "]: " << std::endl; for (int i = 1; i <= k; i++) { // std::cerr << stack[i] - 1 << ", "; transitions.push_back(allTransitions[stack[i] - 1]); @@ -788,32 +1372,59 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st _perfTransTotal++; _perfTransProcessed++; - DUMP_STATS(nrElements); + DUMP_STATS(nrElements, false); - // remove transitions in the same state - if(!filterSameState(transitions)) - continue; - if (dump) DUMP_TRANSSET("after same state filtered"); - - // remove those transitions with a child transition - if(!filterChildEnabled(transitions)) - continue; - if (dump) DUMP_TRANSSET("after child enabled filtered"); + GlobalTransition* transition = NULL; // reduce to conflict-free subset // transitions.to_document_order(); - transitions = removeConflictingTransitions(transitions); - if (dump) DUMP_TRANSSET("after conflicting filtered"); - - // algorithm can never reduce to empty set - assert(transitions.size() > 0); - - // create a GlobalTransition object from the set - GlobalTransition* transition = new GlobalTransition(transitions, _dataModel, this); - if (!transition->isValid) { - // this set of transitions can not be enabled together - delete transition; - continue; + if (!_keepInvalidTransitions) { + // remove transitions in the same state + if(!filterSameState(transitions)) + continue; + if (dump) DUMP_TRANSSET("after same state filtered"); + + // remove those transitions with a child transition +// if(!filterChildEnabled(transitions)) + if(!filterSameHierarchy(transitions)) + continue; + if (dump) DUMP_TRANSSET("after child enabled filtered"); + + transitions = removeConflictingTransitions(transitions); + if (dump) DUMP_TRANSSET("after conflicting filtered"); + // algorithm can never reduce to empty set + assert(transitions.size() > 0); + + // create a GlobalTransition object from the set + transition = new GlobalTransition(transitions, _dataModel, this); + if (!transition->isValid) { + // this set of transitions can not be enabled together + delete transition; + continue; + } + } else { + transition = new GlobalTransition(transitions, _dataModel, this); + + // remove transitions in the same state + if(!filterSameState(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::SAME_SOURCE_STATE; + transition->invalidMsg = "Same source state"; + +// } else if(!filterChildEnabled(transitions)) { + } else if(!filterSameHierarchy(transitions)) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::CHILD_ENABLED; + transition->invalidMsg = "Nested transitions"; + } else { + NodeSet<std::string> nonPreemptingTransitions = removeConflictingTransitions(transitions); + if (nonPreemptingTransitions.size() != transitions.size()) { + transition->isValid = false; + transition->invalidReason = GlobalTransition::PREEMPTING_MEMBERS; + transition->invalidMsg = "Preempting members"; + } + } + } // two combinations might have projected onto the same conflict-free set @@ -830,6 +1441,7 @@ void ChartToFSM::getPotentialTransitionsForConf(const Arabica::XPath::NodeSet<st // std::cerr << "New conflict-free subset: " << transition->transitionId << ":" << transition->eventDesc << std::endl; outMap[transition->transitionId] = transition; } +// _confToTransitions[""] = outMap; return; } @@ -853,7 +1465,11 @@ void ChartToFSM::explode() { // append new global states and pop from front while(statesRemaining.size() > 0) { - DUMP_STATS(0); + _perfStackSize = statesRemaining.size(); + _perfStatesTotal++; + _perfStatesProcessed++; + + DUMP_STATS(0, false); GlobalState* globalState = statesRemaining.front().second; _currGlobalTransition = statesRemaining.front().first; @@ -875,8 +1491,6 @@ void ChartToFSM::explode() { continue; // we have already been here } - _perfStatesProcessed++; - _configuration = globalState->getActiveStates(); _alreadyEntered = globalState->getAlreadyEnteredStates(); _historyValue = globalState->getHistoryStates(); @@ -910,7 +1524,12 @@ void ChartToFSM::explode() { } else { // we need to calculate the potential optimal transition sets std::map<std::string, GlobalTransition*> transitionSets; - getPotentialTransitionsForConf(refsToStates(globalState->activeStatesRefs), transitionSets); + // std::cerr << globalState->stateId << std::endl; + if (_transitionsFromTree) { + getPotentialTransitionsForConfFromTree(refsToStates(globalState->activeStatesRefs), transitionSets); + } else { + getPotentialTransitionsForConfFromPowerSet(refsToStates(globalState->activeStatesRefs), transitionSets); + } // reduce and sort transition sets for(std::map<std::string, GlobalTransition*>::iterator transSetIter = transitionSets.begin(); @@ -920,10 +1539,20 @@ void ChartToFSM::explode() { } globalState->sortedOutgoing.sort(PtrComp<GlobalTransition>); - globalState->sortedOutgoing.unique(hasUnconditionalSuperset); - globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); +// globalState->sortedOutgoing.unique(hasUnconditionalSuperset); +// globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); // unique is not quite like what we need, but it was a start - globalState->sortedOutgoing = reapplyUniquePredicates(globalState->sortedOutgoing); + if (_keepInvalidTransitions) { + globalState->sortedOutgoing = redundantMark(globalState->sortedOutgoing); + } else { +// globalState->sortedOutgoing.unique(hasUnconditionalSuperset); +// globalState->sortedOutgoing.unique(hasEarlierUnconditionalMatch); + globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); + } +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); +// globalState->sortedOutgoing = redundantRemove(globalState->sortedOutgoing); +// +// std::cout << globalState->sortedOutgoing.size() << std::endl; assert(_activeConf.find(globalState->activeId) == _activeConf.end()); assert(globalState->activeIndex == -1); @@ -940,11 +1569,15 @@ void ChartToFSM::explode() { GlobalTransition* outgoingTrans = *transIter; outgoingTrans->source = globalState->stateId; + + if (_keepInvalidTransitions && !outgoingTrans->isValid) + continue; + _currGlobalTransition = outgoingTrans; microstep(refsToTransitions(outgoingTrans->transitionRefs)); - assert(isLegalConfiguration(_configuration)); - +// assert(isLegalConfiguration(_configuration)); + _perfMicroStepProcessed++; _perfMicroStepTotal++; @@ -1086,6 +1719,26 @@ void ChartToFSM::beforeEnteringState(Interpreter interpreter, const Arabica::DOM void ChartToFSM::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, bool moreComing) { } +std::ostream& operator<< (std::ostream& os, const GlobalTransition::Action& action) { + if (action.onEntry) + os << "onEntry: " << action.onEntry; + if (action.onExit) + os << "onExit: " << action.onExit; + if (action.transition) + os << "transition: " << action.transition; + if (action.entered) + os << "entered: " << action.entered; + if (action.exited) + os << "exited: " << action.exited; + if (action.invoke) + os << "invoke: " << action.invoke; + if (action.uninvoke) + os << "uninvoke: " << action.uninvoke; + if (action.raiseDone) + os << "raiseDone: " << action.raiseDone; + return os; +} + GlobalState::GlobalState(const Arabica::XPath::NodeSet<std::string>& activeStates_, const Arabica::XPath::NodeSet<std::string>& alreadyEnteredStates_, // we need to remember for binding=late const std::map<std::string, Arabica::XPath::NodeSet<std::string> >& historyStates_, @@ -1181,6 +1834,11 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t bool foundWithTarget = false; bool foundTargetLess = false; + Arabica::DOM::Element<std::string> withEvent; + Arabica::DOM::Element<std::string> noneEvent; + Arabica::DOM::Element<std::string> withTarget; + Arabica::DOM::Element<std::string> noneTarget; + for (int i = 0; i < transitionSet.size(); i++) { Arabica::DOM::Element<std::string> transElem = Arabica::DOM::Element<std::string>(transitionSet[i]); if (HAS_ATTR(transElem, "eventexpr")) { @@ -1188,19 +1846,23 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t } if (HAS_ATTR(transElem, "event")) { foundWithEvent = true; + withEvent = transElem; if (foundEventLess) break; } else { foundEventLess = true; + noneEvent = transElem; if (foundWithEvent) break; } if (HAS_ATTR(transElem, "target")) { foundWithTarget = true; + withTarget = transElem; if (foundTargetLess) break; } else { foundTargetLess = true; + noneTarget = transElem; if (foundWithTarget) break; } @@ -1209,6 +1871,10 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t // do not mix eventless and event transitions if (foundEventLess && foundWithEvent) { + if (flattener->_keepInvalidTransitions) { + invalidReason = MIXES_EVENT_SPONTANEOUS; + invalidMsg = "Mixes (non-)spontaneous"; + } isValid = false; return; } @@ -1228,6 +1894,10 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t eventNames = getCommonEvents(transitionSet); if (eventNames.size() == 0) { // LOG(INFO) << "No event will activate this conflict-free subset" << std::endl; + if (flattener->_keepInvalidTransitions) { + invalidReason = NO_COMMON_EVENT; + invalidMsg = "No common event"; + } isValid = false; return; } else { @@ -1264,20 +1934,20 @@ GlobalTransition::GlobalTransition(const Arabica::XPath::NodeSet<std::string>& t // std::cout << std::endl << std::endl; } - int index = 0; seperator = ""; for (std::vector<Element<std::string> >::iterator transIter = interpreter->indexedTransitions.begin(); transIter != interpreter->indexedTransitions.end(); transIter++) { const Element<std::string>& refTrans = *transIter; + if (!HAS_ATTR(refTrans, "priority")) + continue; if (InterpreterImpl::isMember(refTrans, transitionSet)) { - members += seperator + toStr(index); + members += seperator + ATTR(refTrans, "priority"); } else { members += seperator; - for (int i = 0; i < toStr(index).size(); i++) { + for (int i = 0; i < ATTR(refTrans, "priority").size(); i++) { members += " "; } } seperator = " "; - index++; } // if (members == " 4 6 7 ") |