def extractTransition(self, line): """Given a line in a dpda description, return a new Transition object described by that line. Overrides the same method in TuringMachine. We need to override because the format is slightly different for dpdas, compared to Turing machines. Args: line (str): a line in a dpda description Returns: Transition: a new Transition object """ (label, stackWriteStr, sourceState, destState) = \ self.splitTransition(line) if TuringMachine.actionSeparator in stackWriteStr: raise WcbcException('transition ' + line + \ ' specifies a direction, which is illegal in a pda') if TuringMachine.actionSeparator not in label: raise WcbcException('transition ' + line + \ ' needs both a label and a stack symbol, which can be ' + \ Nfa.epsilonStr) (label, stackLabel) = [x.strip() for x in \ label.split(TuringMachine.actionSeparator)] if label == Nfa.epsilonStr: direction = TuringMachine.stayDir else: direction = TuringMachine.rightDir return PdaTransition(sourceState, destState, label, \ stackLabel, stackWriteStr, direction)
def addEdge(self, edge, weight = 1): """Add an edge to the graph. An exception is thrown if the edge is already present. An exception is also thrown if the edge contains a node that is not in the graph. Args: edge (Edge): the edge to be added weight (int): the weight of the edge to be added """ node1, node2 = edge.nodes if self.containsEdge(edge): raise WcbcException('edge ' + str(edge) + ' already in graph') if not node1 in self: raise WcbcException('node ' + node1 + ' not in graph') if not node2 in self: raise WcbcException('node ' + node2 + ' not in graph') self.nodes[node1][node2] = weight if not self.directed: self.nodes[node2][node1] = weight self.isolatedNodes = None # force recomputation later, when needed
def readDescription(self, graphString): """Read the given ASCII description of a graph, storing information about the nodes and edges. This is intended to be a private method called by __init__(). It will update self.nodes, which should be an empty dictionary when this method is called. Args: graphString (str): a description of the graph as an ASCII string, as described in the textbook. Examples include 'a,b b,c c,a' and 'a,b,4 b,c,3'. """ edgeDescriptions = [x.strip() for x in graphString.split()] for edgeDescription in edgeDescriptions: if len(edgeDescription)>0: components = edgeDescription.split(',') if len(components)==1: # isolated node with no edges (yet) (sourceStr, destStr, weightStr) = (components[0], None, None) elif self.weighted: if len(components)!=3: raise WcbcException('expected 3 components in edge description ' \ + edgeDescription + \ 'for weighted graph') (sourceStr, destStr, weightStr) = edgeDescription.split(',') weight = int(weightStr) else: if len(components)!=2: raise WcbcException('expected 2 components in edge description ' \ + edgeDescription + \ 'for unweighted graph') (sourceStr, destStr) = edgeDescription.split(',') weight = 1 if len(sourceStr)==0 or (destStr and len(destStr)==0): raise WcbcException('encountered node name of length zero') if not sourceStr in self.nodes: self.nodes[sourceStr] = dict() source = self.nodes[sourceStr] if destStr!=None: if not destStr in self.nodes: # we haven't seen this node before -- create it self.nodes[destStr] = dict() if destStr in source: raise WcbcException('duplicate edge ' + str([sourceStr, destStr])) source[destStr] = weight if not self.directed: dest = self.nodes[destStr] if sourceStr in dest and sourceStr!=destStr: raise WcbcException('duplicate edge ' + str([destStr, sourceStr])) dest[sourceStr] = weight
def cycleLength(self, cycle): """Return the "length" of the given cycle (i.e. total weight of its edges) For unweighted graphs, the length of the cycle is the number of edges in it. For weighted graphs, the "length" is the total weight of its edges. If the given cycle is not in fact a cycle in the current graph, an exception is raised. See the important note in the documentation for isCycle(): the given cycle parameter should not explicitly contain the final edge back to the start of the cycle; it will be added implicitly. Args: cycle (Path): the sequence p of nodes to be investigated Returns: int: the total weight of the edges in the cycle """ if not self.isCycle(cycle): raise WcbcException(str(cycle) + ' is not a cycle in the graph ' + str(self)) if len(cycle)==0: return 0 pathLen = self.pathLength(cycle) finalEdge = Edge([cycle.end(), cycle.start()]) return pathLen + self.getWeight(finalEdge)
def fird(inString): split = inString.split() if len(split) != 3: raise WcbcException("input must be composed of 3 numbers") M, a, b = int(split[0]), int(split[1]), int(split[2]) if a > b: raise WcbcException("a must be equal to or smaller than b") for i in range(max(a, 2), min(b + 1, M)): if M % i == 0: return "yes" return "no"
def end(self): """Return the final node in the path. Returns: str: the final node in the path. """ if len(self.nodes)==0: raise WcbcException('can\'t find end of empty path') return self.nodes[-1]
def start(self): """Return the initial node in the path. Returns: str: the initial node in the path. """ if len(self.nodes)==0: raise WcbcException('can\'t find start of empty path') return self.nodes[0]
def __init__(self, nodes): """Initialize Path object. Args: nodes (list of str): a list containing the names of the nodes in the path """ if isinstance(nodes, str): raise WcbcException('Path constructor parameter must be a list, not a string') for node in nodes: if not isinstance(node, str): raise WcbcException('Path node', str(node), 'isn\'t a string') if len(node)==0: raise WcbcException('Encountered empty node name') self.nodes = tuple(nodes) """Tuple of the nodes in the path, where each node is a string.""" self.nodeSet = None """A set of str that is constructed lazily (if and when needed). It will contain the nodes in the path""" self.edgeSet = None """Set of Edge objects in the path.
def addNode(self, node): """Add the given node to the graph. The node is added to the graph as an isolated node with no incoming or outgoing edges. Args: node (str): The node to be added. """ if node in self.nodes: raise WcbcException('Tried to add existing node ' + node) self.nodes[node] = dict() self.isolatedNodes = None # force recomputation later, when needed
def getWeight(self, edge): """Return the weight of the given edge. An exception is thrown if the edge is not present. Args: edge (Edge): the edge to be searched for Returns: int: The weight of the given edge. """ if not self.containsEdge(edge): raise WcbcException('edge ' + str(edge) + ' not in graph') node1, node2 = edge.nodes return self.nodes[node1][node2]
def removeEdge(self, edge): """Remove an edge from the graph. An exception is thrown if the edge is not already present. This implicitly requires that both nodes in the edge are also present. Args: edge (Edge): the edge to be removed """ node1, node2 = edge.nodes if not self.containsEdge(edge): raise WcbcException('edge ' + str(edge) + ' not in graph') if node2 in self.nodes[node1]: del self.nodes[node1][node2] if not self.directed: if node1 in self.nodes[node2]: del self.nodes[node2][node1] self.isolatedNodes = None # force recomputation later, when needed
def tryAllPrefixExtensions(graph, prefix, prefixDistance): if len(graph) == len(prefix): raise WcbcException( 'tryAllPrefixExtensions() received a path that includes every node' ) lastNode = prefix.end() bestCycle = None bestDistance = None for nextNode in graph.neighbors(lastNode): if nextNode not in prefix: extendedPrefix = prefix.extend(nextNode) extendedPrefixDistance = prefixDistance + graph.getWeight( Edge([lastNode, nextNode])) (cycle, distance) = shortestCycleWithPrefix(graph, extendedPrefix, extendedPrefixDistance) if cycle != None: if bestDistance == None or distance < bestDistance: bestDistance = distance bestCycle = cycle return (bestCycle, bestDistance)
def weightedNeighbors(self, node): """Return a dictionary of the neighbors of a given node with weights as keys. The neighbors of node n are defined to be all nodes that are at the end of outgoing edges from n. Args: node (str): The node n whose neighbors will be returned. Returns: dictionary mapping str to int: Each key in the dictionary is a neighbor m of n, i.e. there is an edge from n to m. The value corresponding to key m is the weight of the edge n to m. """ if not node in self: raise WcbcException('node ' + node + ' not in graph') return self.nodes[node]
def rotateToFront(self, node): """Cyclically permute the nodes in the path so that a given node is at the start of the path. Args: node (str): The node which should be at the start of the path. An exception will be thrown if it is not currently in the path. Returns: Path: A new Path object, cyclically permuted so the given node is at the start of the path. """ if not node in self.nodes: raise WcbcException('node ' + str(node) + ' is not in the path ' + str(self)) ind = self.nodes.index(node) newNodes = self.nodes[ind:] + self.nodes[:ind] return Path(newNodes)
def pathLength(self, path): """Return the "length" of the given path (i.e. total weight of its edges) For unweighted graphs, the length of the path is the number of edges in it. For weighted graphs, the "length" is the total weight of its edges. If the given path is not in fact a path in the current graph, an exception is raised. Args: path (Path): the sequence p of nodes to be investigated Returns: int: the total weight of the edges in the path """ if not self.isPath(path): raise WcbcException(str(path) + ' is not a path in the graph ' + str(self)) length = 0 for edge in path: length += self.getWeight(edge) return length
def neighbors(self, node): """Return a set-like view of the neighbors of a given node. The neighbors of node n are defined to be all nodes that are at the end of outgoing edges from n. Args: node (str): The node whose neighbors will be returned. Returns: dictionary view of str: The returned object is a dictionary view (see Python documentation on "dictionary view objects"). Iterating over this view will yield all nodes at the end of outgoing edges from the given parameter node. """ if not node in self: raise WcbcException('node ' + node + ' not in graph') return self.nodes[node].keys()