def support_exist(self, expr1, expr2, lst=False): if ((expr1 is None) or (expr1 == "")) or ((expr2 is None) or (expr2 == "")): return [] if lst else None variables = re.sub('[~\&\|\(\)]', ' ', expr1 + expr2) variables = re.sub(' +', ' ', variables.strip()) variables = variables.split(" ") bdd = BDD() for var in variables: bdd.add_var(var) expr1 = bdd.add_expr(expr1) expr2 = bdd.add_expr(expr2) u = bdd.exist(bdd.support(expr1), expr2) dnf = self.__get_dnf(bdd, u) conj = [] for el in dnf: if el[0] is True: el = el[1:] el.reverse() conj.append("(%s)" % " & ".join(el)) if lst: return conj return " | ".join(conj)
class UVW: def __init__(self,ddMgr = None): """Initializes the UVW. There is always a fail state with no. 0""" self.stateNames = ["reject"] # Init with reject stat self.rejecting = [True] if ddMgr is None: self.ddMgr = BDD() else: self.ddMgr = ddMgr self.transitions = [[(0,self.ddMgr.true)]] self.initialStates = [] self.propositions = [] self.labelToStateAndActivatingMapper = {} def inGameSolverFormat(self, singleAction = False): resultLines = [] if singleAction: allowedActions = self.ddMgr.true # At most one variable is true for varName in self.ddMgr.vars: for varNameOther in self.ddMgr.vars: if varNameOther!=varName: allowedActions = allowedActions & ( ~(self.ddMgr.var(varNameOther)) | ~(self.ddMgr.var(varName))) # One of them is True thisOne = self.ddMgr.false for varName in self.ddMgr.vars: thisOne = thisOne | self.ddMgr.var(varName) # allowedActions = allowedActions & thisOne <--- There could be actions not in the spec #Define STATES for i,a in enumerate(self.stateNames): flags = [] if i in self.initialStates: flags.append("initial") if self.rejecting[i]: flags.append("reject") flags = " ".join(flags) if len(flags)>0: flags = " "+flags resultLines.append("State **TYPE** "+"q"+str(i)+flags) # Define transitions for i,a in enumerate(self.stateNames): for (j,b) in self.transitions[i]: while b!=self.ddMgr.false: # Generate minterm for transition rest = b conditionParts = [] if singleAction: if b==allowedActions: conditionParts = ["TRUE"] rest = self.ddMgr.true else: for p in self.propositions: if ~(self.ddMgr.var(p)) & b == ~(self.ddMgr.var(p)) & allowedActions: conditionParts = ["! "+p] rest = self.ddMgr.true if conditionParts==[]: for p in self.propositions: if not (self.ddMgr.var(p) & b == self.ddMgr.false): conditionParts = [p] rest = self.ddMgr.var(p) if conditionParts==[]: # All other remaining case - Is there any? print("C3",file=sys.stderr) for p in self.propositions: if self.ddMgr.forall([p],(rest & self.ddMgr.var(p)))!=rest: if (rest & self.ddMgr.var(p))==self.ddMgr.false: conditionParts.append("! "+p) rest = rest & ~(self.ddMgr.var(p)) else: conditionParts.append(p) rest = rest & (self.ddMgr.var(p)) else: for p in self.propositions: if self.ddMgr.forall([p],(rest & self.ddMgr.var(p)))!=rest: if (rest & self.ddMgr.var(p))==self.ddMgr.false: conditionParts.append("! "+p) rest = rest & ~(self.ddMgr.var(p)) else: conditionParts.append(p) rest = rest & (self.ddMgr.var(p)) b = b & ~rest if len(conditionParts)>0: # We know that there can only be one event at a time -- Remove negated ones if possible label = "& "*(len(conditionParts)-1)+" ".join(conditionParts) resultLines.append("Transition **TYPE** q"+str(i)+" "+label+" q"+str(j)) else: resultLines.append("Transition **TYPE** q"+str(i)+" TRUE q"+str(j)) return "\n".join(resultLines) def toNeverClaim(self): # Never claim header resultLines = ["never { /* Subformulas covered by this Never Claim:"] for i in self.initialStates: if len(self.stateNames[i])>100: resultLines.append(" - "+self.stateNames[i][0:100]+"...") else: resultLines.append(" - "+self.stateNames[i]) resultLines.append(" */") # Assign state names neverClaimStateNames = ["T"+str(i) for i in range(0,len(self.transitions))] assert self.rejecting[0] neverClaimStateNames[0] = "all" for i in self.initialStates: neverClaimStateNames[i] = neverClaimStateNames[i]+"_init" for i in range(0,len(self.transitions)): if self.rejecting[i]: neverClaimStateNames[i] = "accept_"+neverClaimStateNames[i] # Other states for i in range(1,len(self.transitions)): resultLines.append(neverClaimStateNames[i]+":") resultLines.append(" // "+str(self.stateNames[i])) resultLines.append(" if") for (a,b) in self.transitions[i]: while b!=self.ddMgr.false: # Generate minterm for transition rest = b conditionParts = [] for p in self.propositions: if self.ddMgr.exist([p],(rest & self.ddMgr.var(p)))!=rest: if (rest & self.ddMgr.var(p))==self.ddMgr.false: conditionParts.append("! "+p) rest = rest & ~(self.ddMgr.var(p)) else: conditionParts.append(p) rest = rest & (self.ddMgr.var(p)) b = b & ~rest if len(conditionParts)>0: resultLines.append(" :: ("+" && ".join(conditionParts)+") -> goto "+neverClaimStateNames[a]) else: resultLines.append(" :: (1) -> goto "+neverClaimStateNames[a]) resultLines.append(" fi") # First state is special case resultLines.append(neverClaimStateNames[0]+":\n skip") resultLines.append("}") return "\n".join(resultLines) @staticmethod def isBisimulationEquivalent(uvw1Pre,uvw2): # Make a copy as we are going to modify it - but the BDD manager must be the same uvw1 = UVW(uvw1Pre.ddMgr) uvw1.stateNames = copy.copy(uvw1Pre.stateNames) uvw1.rejecting = copy.copy(uvw1Pre.rejecting) uvw1.transitions = copy.copy(uvw1Pre.transitions) uvw1.initialStates = copy.copy(uvw1Pre.initialStates) uvw1.propositions = None # Not needed in the following - this is a temporary copy # Merge the new states in / the initial states stay unmerged oldNofStates = len(uvw1.rejecting) uvw1.rejecting = uvw1.rejecting + uvw2.rejecting uvw1.stateNames = uvw1.stateNames + uvw2.stateNames for i in range(0,len(uvw2.transitions)): uvw1.transitions.append([(a+oldNofStates,b) for (a,b) in uvw2.transitions[i]]) # Get simulation relation (bottomUpOrder,reverseBottomUpOrder) = uvw1.getBottomUpAndReverseBottomUpOrder() backwardSimulation = uvw1.computeSimulationRelation(bottomUpOrder,reverseBottomUpOrder) # Check now that for each initial state in A there is one simulating one in B otherInitialStates = [a+oldNofStates for a in uvw2.initialStates] for (reference,duplicates) in [(uvw1.initialStates,otherInitialStates),(otherInitialStates,uvw1.initialStates)]: for a in reference: found = False for b in duplicates: if backwardSimulation[reverseBottomUpOrder[a]][reverseBottomUpOrder[b]]: found = True if not found: return False # Found no counter-example ---> equivalent return True @staticmethod def parseFromUVWDescription(ddMgr,description): """Parses a UVW from a textual description consisting of the transitions of the UVW, where state names from which a transition originate can be labeled by whether they are rejecting and initial.""" uvw = UVW(ddMgr) transitions = description.split(",") if transitions==[""]: # Special case: Nothing to parse ---> No transition transitions = [] for transition in transitions: transition = transition.strip() (fromState,rest) = tuple(transition.split("-[")) (condition,targetState) = tuple(rest.split("]->")) fromStateAttributes="" if "(" in fromState: (fromState,fromStateAttributes) = tuple(fromState.split("(")) fromStateNo = int(fromState) toStateNo = int(targetState) # Make new state while len(uvw.rejecting)<=max(fromStateNo,toStateNo): newStateNr = len(uvw.rejecting) uvw.stateNames.append(str(newStateNr)) uvw.rejecting.append(False) uvw.transitions.append([]) # Add from state attributes for a in fromStateAttributes: if a==")" or a==",": pass elif a=="i": if not fromStateNo in uvw.initialStates: uvw.initialStates.append(fromStateNo) elif a=="r": uvw.rejecting[fromStateNo] = True else: raise Exception("Unknown state attribute:"+str(a)) # Parse transition uvw.transitions[fromStateNo].append((toStateNo,uvw.ddMgr.add_expr(condition))) return uvw def __str__(self): assert len(self.rejecting) == len(self.stateNames) assert len(self.transitions) == len(self.stateNames) result = "UVW with " + str(len(self.rejecting))+" states and initial states "+str(self.initialStates)+":\n" for i in range(0,len(self.rejecting)): result = result + " - State No. "+str(i)+" with name "+self.stateNames[i] if self.rejecting[i]: result = result + " (rej.)" result = result + ":\n" for a in self.transitions[i]: result = result + " - t.t.s. "+str(a[0])+" for "+a[1].to_expr()+"\n" return result def __del__(self): # First delete the transitions, as only then, all BDD nodes are freed. del self.transitions self.ddMgr = None def addStateButNotNewListOfTransitionsForTheNewState(self,stateName,rejecting=False): self.stateNames.append(stateName) self.rejecting.append(rejecting) return len(self.stateNames)-1 def makeTransientStatesNonRejecting(self): for i in range(0,len(self.stateNames)): if self.rejecting[i]: foundLoop = False for (a,b) in self.transitions[i]: if a==i: foundLoop = True if not foundLoop: self.rejecting[i] = False def restrictToTheCaseThatThereCanOnlyBeOneActionAtATime(self): # Build BDD for the allowed character combinations allowedActions = self.ddMgr.true # At most one variable is true for varName in self.ddMgr.vars: for varNameOther in self.ddMgr.vars: if varNameOther!=varName: allowedActions = allowedActions & ( ~(self.ddMgr.var(varNameOther)) | ~(self.ddMgr.var(varName))) # One of them is True thisOne = self.ddMgr.false for varName in self.ddMgr.vars: thisOne = thisOne | self.ddMgr.var(varName) # allowedActions = allowedActions & thisOne <---- There could be actions not mentioned in the spec # Clean up the transitions oldTransitions = self.transitions for fromStateNo in range(0,len(oldTransitions)): t = [] for (toStateNo,expr) in oldTransitions[fromStateNo]: expr = expr & allowedActions if expr != self.ddMgr.false: t.append((toStateNo,expr)) uvw.transitions[fromStateNo] = t def removeUnreachableStates(self): # Obtain list of reachable states # Assumes that there are no transitions labeled by FALSE reachable = [False for i in self.rejecting] reachable[0] = True todo = copy.copy(self.initialStates) for a in self.initialStates: reachable[a] = True while len(todo)>0: thisOne = todo[0] todo = todo[1:] for (a,b) in self.transitions[thisOne]: assert b!=self.ddMgr.false if not reachable[a]: reachable[a] = True todo.append(a) # Get renumeration list stateMapper = [] stateMapperSoFar = 0 for i in range(0,len(reachable)): if reachable[i]: stateMapper.append(stateMapperSoFar) stateMapperSoFar += 1 else: stateMapper.append(None) # Move state numbers self.labelToStateAndActivatingMapper = {} # Purge as this modfies the automaton newStateNames = [self.stateNames[i] for i in range(0,len(self.stateNames)) if reachable[i]] self.stateNames = newStateNames # 2. Rejecting newRejecting = [self.rejecting[i] for i in range(0,len(self.rejecting)) if reachable[i]] self.rejecting = newRejecting # 3. Rejecting newTransitions = [] for i in range(0,len(self.transitions)): if reachable[i]: newTransitions.append([]) for (a,b) in self.transitions[i]: newTransitions[-1].append((stateMapper[a],b)) self.transitions = newTransitions # 4. Initial states self.initialStates = [stateMapper[a] for a in self.initialStates] def getBottomUpAndReverseBottomUpOrder(self): # 1. Compute a bottom-up ordering of all states bottomUpOrder = [] while len(bottomUpOrder)!=len(self.stateNames): for i in range(0,len(self.stateNames)): allDone = reduce(lambda x,y: x and y, [a in bottomUpOrder or a==i for (a,b) in self.transitions[i]]) if not i in bottomUpOrder and allDone: bottomUpOrder.append(i) # 1b. Reverse bottom up order reverseBottomUpOrder = copy.copy(bottomUpOrder) for i in range(0,len(bottomUpOrder)): reverseBottomUpOrder[bottomUpOrder[i]] = i return (bottomUpOrder,reverseBottomUpOrder) def computeSimulationRelation(self,bottomUpOrder,reverseBottomUpOrder): # 2. Compute simulation relation simulationRelation = [] for orderPosA in range(0,len(bottomUpOrder)): simulationForA = [] simulationRelation.append(simulationForA) stateA = bottomUpOrder[orderPosA] for orderPosB in range(0,len(bottomUpOrder)): stateB = bottomUpOrder[orderPosB] # Check whether state A accepts at least as much as state B does # This means that for every action in automaton A, there must be one in B to a state that is at least as restrictive. # Double-Self-loops are fine if we do not have that state A is rejecting but state B is not simIsFine = True for (dest,destCond) in self.transitions[stateA]: # Try to discharge all elements of destCond restCond = destCond for (destB,destCondB) in self.transitions[stateB]: if dest==stateA and destB==stateB: if (not self.rejecting[dest]) or self.rejecting[destB]: restCond = restCond & ~(destCond & destCondB) else: posA = reverseBottomUpOrder[dest] posB = reverseBottomUpOrder[destB] if simulationRelation[posA][posB]: restCond = restCond & ~(destCond & destCondB) simIsFine = simIsFine and (restCond == self.ddMgr.false) simulationForA.append(simIsFine) return simulationRelation def findReachabilityImplyingStates(self,bottomUpOrder: typing.List[int]) -> typing.List[typing.List[bool]]: """Computes a list a such that a[i][j] is True whenever being in state i implies also being in state j.""" reachabilityImplications = [[False for i in range(0,len(bottomUpOrder))] for j in range(0,len(bottomUpOrder))] # Compute inverse transitions inverseTransitions = [[] for i in bottomUpOrder] for i in range(0,len(self.transitions)): for (a,b) in self.transitions[i]: inverseTransitions[a].append((i,b)) # Compute reachability implying states for aIndex in range(len(bottomUpOrder)-1,-1,-1): for bIndex in range(len(bottomUpOrder)-1,-1,-1): aState = bottomUpOrder[aIndex] bState = bottomUpOrder[bIndex] # Check if for every incoming transition to state "a", there is # a corresponding on the state "b" foundUnimplyingCase = False # Check initial if (aState in self.initialStates) and not (bState in self.initialStates): foundUnimplyingCase = True # Check the rest for (a,b) in inverseTransitions[aState]: rest = b for (c,d) in inverseTransitions[bState]: # Self-loops must be contained if (a==aState) and (c==bState): rest = rest & ~d elif reachabilityImplications[a][c]: rest = rest & ~d if rest!=self.ddMgr.false: foundUnimplyingCase = True # Store results reachabilityImplications[aState][bState] = not foundUnimplyingCase return reachabilityImplications def computeTransitiveClosureOfTransitionRelation(self): result = set([]) for a in range(0,len(self.transitions)): result.add((a,a)) for i,a in enumerate(self.transitions): for (b,c) in a: result.add((i,b)) lastResult = set([]) while len(result)!=len(lastResult): lastResult = result result = set([]) for (a,b) in lastResult: result.add((a,b)) for (c,d) in lastResult: if c==b: result.add((a,d)) return result def mergeEquivalentlyReachableStates(self): # 1. Compute a bottom-up ordering of all states (bottomUpOrder,reverseBottomUpOrder) = self.getBottomUpAndReverseBottomUpOrder() # 2. Compute simulation relation forwardSimulation = self.findReachabilityImplyingStates(bottomUpOrder) # 2b. Compute transitive closure of transition relation transitiveClosureOfTransitionRelation = self.computeTransitiveClosureOfTransitionRelation() # 3. Print forward simulation # for i in range(0,len(self.transitions)): # for j in range(0,len(self.transitions)): # if forwardSimulation[i][j]: # print("REachImply: "+str(bottomUpOrder[i])+","+str(bottomUpOrder[j])) #print("Bottom Up Order:",bottomUpOrder) #print("Closure:",transitiveClosureOfTransitionRelation) # 4. Forward bisimulation merge State Mapper Computation stateMapper = {} for j in range(len(bottomUpOrder)-1,-1,-1): for i in range(0,j): if forwardSimulation[bottomUpOrder[i]][bottomUpOrder[j]] and forwardSimulation[bottomUpOrder[j]][bottomUpOrder[i]]: if not bottomUpOrder[i] in stateMapper: if not (bottomUpOrder[j],bottomUpOrder[i]) in transitiveClosureOfTransitionRelation: stateMapper[bottomUpOrder[i]] = bottomUpOrder[j] # print("StateMapper: ",stateMapper) # print("This means: ",[(bottomUpOrder[a],bottomUpOrder[b]) for (a,b) in stateMapper.items()]) # 5. Redirect for i in range(0,len(bottomUpOrder)): if i in stateMapper: self.stateNames[stateMapper[i]] = "& "+ self.stateNames[stateMapper[i]] + " " + self.stateNames[i] for (a,b) in self.transitions[i]: self.transitions[stateMapper[i]].append((a,b)) self.transitions[i] = [] # 6. Clean up self.removeStatesWithoutOutgoingTransitions() self.mergeTransitionsBetweenTheSameStates() self.removeUnreachableStates() def removeForwardReachableBackwardSimulatingStates(self): # 1. Compute a bottom-up ordering of all states (bottomUpOrder,reverseBottomUpOrder) = self.getBottomUpAndReverseBottomUpOrder() # 2. Compute simulation relation --> Indices are over the state numbers! forwardSimulation = self.findReachabilityImplyingStates(bottomUpOrder) # 2. Compute backward simulation relation --> Indices are over the order in the bottom up order backwardSimulation = self.computeSimulationRelation(bottomUpOrder,reverseBottomUpOrder) # 3. Find out which state is reachable from which other states reachabilityRelation = [] for i in range(0,len(bottomUpOrder)): reachable = set([i]) todo = set([i]) while len(todo)>0: thisOne = todo.pop() for (a,b) in self.transitions[thisOne]: if not a in reachable: assert b!=self.ddMgr.false reachable.add(a) todo.add(a) thisList = [] for a in range(0,len(bottomUpOrder)): thisList.append(a in reachable) reachabilityRelation.append(thisList) # Print reachability relation # print("Reachability Relation:") # for a in range(0,len(bottomUpOrder)): # for b in range(0,len(bottomUpOrder)): # if reachabilityRelation[a][b]: # print ("Reach:",a,b) # 3. Print forward simulation # for i in range(0,len(self.transitions)): # for j in range(0,len(self.transitions)): # if forwardSimulation[i][j]: # print("REachImply: "+str(i)+","+str(j)) # print("Simulation:") # for orderPosA in range(0,len(bottomUpOrder)): # for orderPosB in range(0,len(bottomUpOrder)): # if (backwardSimulation[orderPosA][orderPosB]): # print("States "+str(bottomUpOrder[orderPosA])+" "+str(bottomUpOrder[orderPosB])+" sim\n") # 4. Remove states that are removable for backwardSimulationPositionA in range(0,len(backwardSimulation)): for backwardSimulationPositionB in range(0,len(backwardSimulation)): if backwardSimulationPositionA!=backwardSimulationPositionB: if backwardSimulation[backwardSimulationPositionA][backwardSimulationPositionB]: # We know that A accepts at least as much as B stateA = bottomUpOrder[backwardSimulationPositionA] stateB = bottomUpOrder[backwardSimulationPositionB] sys.stdout.flush() if forwardSimulation[stateA][stateB]: # We know that B is reached at least as easily as state A # print("Merging candidates",stateA,"and",stateB,file=sys.stderr) # old: # if not reachabilityRelation[stateA][stateB] and not reachabilityRelation[stateB][stateA]: if not reachabilityRelation[stateB][stateA]: # Get rid of A # print("Removing state",stateA,"because of state ",stateB,file=sys.stderr) self.transitions[stateA] = [] # Reroute all transitions to State A to State B for i in range(0,len(self.transitions)): self.transitions[i] = [(a if a!=stateA else stateB,b) for (a,b) in self.transitions[i]] elif not reachabilityRelation[stateA][stateB] and self.rejecting[stateA] == self.rejecting[stateB]: # print("Merger:",self.rejecting,file=sys.stderr) # print("old uvw:",self,file=sys.stderr) self.transitions[stateB].extend([(stateB,cond) for (a,cond) in self.transitions[stateA] if a==stateA]) self.transitions[stateB].extend([(a,cond) for (a,cond) in self.transitions[stateA] if a!=stateA]) self.transitions[stateA] = [] for i in range(0,len(self.transitions)): self.transitions[i] = [(a if a!=stateA else stateB,b) for (a,b) in self.transitions[i]] # Some states may have no outgoing transitions now. Remove them! self.removeStatesWithoutOutgoingTransitions() self.mergeTransitionsBetweenTheSameStates() # State merging could have caused a disbalance here. self.removeUnreachableStates() # State merging could have caused a disbalance here. def removeStatesWithoutOutgoingTransitions(self): """Removes all states without outgoing transitions. Assumes that all FALSE-transitions have already been removed, otherwise some states may remain""" assert len(self.rejecting)==len(self.transitions) assert len(self.rejecting)==len(self.stateNames) emptyStates = [len(self.transitions[i])==0 for i in range(0,len(self.transitions))] stateMapper = {} self.labelToStateAndActivatingMapper = {} # Purge as this modfies the automaton nofStatesSoFar = 0 for i in range(0,len(self.transitions)): if emptyStates[i]: stateMapper[i] = None else: stateMapper[i] = nofStatesSoFar nofStatesSoFar += 1 self.transitions = [[(stateMapper[a],b) for (a,b) in self.transitions[i] if not emptyStates[a]] for i in range(0,len(self.transitions)) if not emptyStates[i]] self.stateNames = [self.stateNames[i] for i in range(0,len(self.stateNames)) if not emptyStates[i]] self.rejecting = [self.rejecting[i] for i in range(0,len(self.rejecting)) if not emptyStates[i]] self.initialStates = [stateMapper[a] for a in self.initialStates if not emptyStates[a]] assert len(self.rejecting)==len(self.transitions) assert len(self.rejecting)==len(self.stateNames) def mergeTransitionsBetweenTheSameStates(self): for i in range(0,len(self.transitions)): allTrans = {} for (a,b) in self.transitions[i]: if a in allTrans: allTrans[a] = allTrans[a] | b else: if b!=self.ddMgr.false: allTrans[a] = b self.transitions[i] = [(a,allTrans[a]) for a in allTrans] def simulationBasedMinimization(self): """Perform simulation-based minimization. States that are bisimilar are always made equivalent""" # 1. Compute a bottom-up ordering of all states (bottomUpOrder,reverseBottomUpOrder) = self.getBottomUpAndReverseBottomUpOrder() # 2. Compute simulation relation simulationRelation = self.computeSimulationRelation(bottomUpOrder,reverseBottomUpOrder) # 3. Print Simulation relation # print("Simulation:") # for orderPosA in range(0,len(bottomUpOrder)): # for orderPosB in range(0,len(bottomUpOrder)): # if (simulationRelation[orderPosA][orderPosB]): # print("States "+str(bottomUpOrder[orderPosA])+" "+str(bottomUpOrder[orderPosB])+" sim\n") # 4. Minimize using bisimulation # -> prepare state mapper stateMapper = {} for j in range(0,len(bottomUpOrder)): found = False for i in range(0,j): if simulationRelation[i][j] and simulationRelation[j][i]: if not found: stateMapper[bottomUpOrder[j]] = bottomUpOrder[i] found = True if not found: stateMapper[bottomUpOrder[j]] = bottomUpOrder[j] # Renumber according to state wrapper for i in range(0,len(bottomUpOrder)): newTransitions = [] for (a,b) in self.transitions[i]: newTransitions.append((stateMapper[a],b)) self.transitions[i] = newTransitions self.initialStates = [stateMapper[a] for a in self.initialStates] def decomposeIntoSimpleChains(self): self.mergeTransitionsBetweenTheSameStates() result = [] def recurse(result,pathSoFar): # print("Rec:",pathSoFar) lastState = pathSoFar[-1] foundSucc = False foundSelfLoop = False for (a,b) in self.transitions[lastState]: if a==lastState: foundSelfLoop=True pathSoFar.append(b) # print("MK:",a,b.to_expr()) if not foundSelfLoop: pathSoFar.append(None) for (a,b) in self.transitions[lastState]: if a!=lastState: foundSucc = True recurse(result,pathSoFar+[b,a]) if not foundSucc: # final! novo = UVW(self.ddMgr) novo.propositions = copy.copy(self.propositions) for i in range(0,(len(pathSoFar)+1)//3): currentState = pathSoFar[i*3] # print("CS:",currentState,pathSoFar[i*3+1]) if currentState!=0: novo.addStateButNotNewListOfTransitionsForTheNewState(self.stateNames[currentState],self.rejecting[currentState]) novo.transitions.append([]) stateNo = len(novo.transitions)-1 else: stateNo = 0 if (i==0): novo.initialStates.append(stateNo) if not pathSoFar[i*3+1] is None: novo.transitions[stateNo].append((stateNo,pathSoFar[i*3+1])) # print("Reg:",pathSoFar[i*3+1].to_expr(),stateNo,i) if len(pathSoFar)>i*3+2: if pathSoFar[i*3+3]==0: nextState = 0 else: nextState = stateNo+1 novo.transitions[stateNo].append((nextState,pathSoFar[i*3+2])) # print("TRANS:",stateNo,nextState,pathSoFar[i*3+2].to_expr()) novo.mergeTransitionsBetweenTheSameStates() result.append(novo) for i in self.initialStates: recurse(result,[i]) return result