class Assembler: def __init__(self, filename, k, errorcorrect=False): #loads file reads = Loader.load(filename) #gets graph self.graph = Graph(reads, k, errorcorrect) self.k = k def make_superpath(self): """loops to make superpaths in graph""" # try without curls while self.superpath_helper(): self.graph.clean() self.graph.clean() def superpath_helper(self): """Does actual merging""" for read in self.graph.readList: for i in range(len(read.edges)-1): #get preceding if possible if i > 0: p = read.edges[i-1] else: p = None #get x,y x = read.edges[i] y = read.edges[i+1] #check if edges can be merged if self.graph.is_mergeable(p,x,y): #make sure merge worked if self.graph.merge(x,y): return True return False def is_eulerian(self): """Checks whether or not the graph is eulerian""" # count semi-balanced nodes (|indegree - outdegree| = 1) semis = 0 for node in self.graph.nodeList: diff = abs(len(node.outgoing)-len(node.incoming)) # if not balanced or semi-balanced, it is not euler if diff > 1: return False elif diff == 1: semis += 1 # not eulerian if more than 2 semi-balanced nodes if semis > 2: return False return True def balance(self): """Connects semi-balanced nodes to eachother This function only works if the graph is eulerian! Returns False if failed. Returns True on success. """ # find unbalanced nodes semis = [] for node in self.graph.nodeList: diff = abs(len(node.outgoing)-len(node.incoming)) if diff == 1: semis += [node] # can we do it? if len(semis) != 2: print("Too many unbalanced: %i. The graph is not eulerian.")%len(semis) return False # find balance if len(semis[0].incoming) > len(semis[0].outgoing): # e.g. needs an outgoing to balance # 0 -> 1 self.graph.new_edge(semis[0], semis[1], semis[0].contents) else: # 1 -> 0 self.graph.new_edge(semis[1], semis[0], semis[1].contents) # balance found return True def eulerian_path(self): """Constructs a eulerian path on the graph using Heirholzer's algorithm """ # init currentPath = [] finalPath = [] # try to start on semi-balanced with less incoming edge = None for node in self.graph.nodeList: diff = abs(len(node.outEdges)-len(node.inEdges)) if diff == 1 and len(node.inEdges) < len(node.outEdges): edge = self.graph.get_unvisited(node) # just pick first if failed if not edge: edge = self.graph.get_unvisited(self.graph.nodeList[0]) # add all edges to stack in linear fashion while edge != None: edge.visited = True currentPath.append(edge) edge = self.graph.get_unvisited(edge.outNode) # next node/edge # get all other unvisted and construct final path while len(currentPath) > 0: edge = currentPath.pop() finalPath.append(edge) edge = self.graph.get_unvisited(edge.inNode) # previous node/edge # loop for unvisited edges again while edge != None: edge.visited = True currentPath.append(edge) edge = self.graph.get_unvisited(edge.outNode) # next node/edge # print result by appending to front sequence = '' while len(finalPath) > 0: edge = finalPath.pop() if len(finalPath) == 0: # last edge sequence += edge.sequence # add all else: sLen = len(edge.sequence) sequence += edge.sequence[:sLen-self.k+1] # add first only return sequence