Esempio n. 1
0
 def _create_digraph(self):
     digraph = DiGraph()
     digraph.add_nodes(self.jobs)
     for edge in self._graph:
         if edge[1] is not None:
             digraph.add_edge(edge)
     self._digraph = digraph
def _generate_directed_graph(int_list):
    g = DirectedGraph()
    max_index = len(int_list) - 1
    g.add_nodes(list(range(len(int_list))))

    for i, x in enumerate(int_list):
        if i + x <= max_index:
            g.add_edge((i, i + x))
        if i - x >= 0:
            g.add_edge((i, i - x))

    return g
Esempio n. 3
0
	def __init__(self):
		self.digraph = Digraph()
		
		# A map of human readable labels to help users
		self.nodemap = {} # {'label' : <node object> }
		
		# A dictionary of nodes evaluated data. Values will be None if node needs evaluating
		self.cache = {} # {'label' : {data} }
		
		# A dictionary of errors associated with each node
		self.errors = {} # {'label' : [ "errors"]}
		
		# a set of updated nodes since last DAG evaluation
		self.updated = set() # ['labels']
Esempio n. 4
0
class DAG(object):
	def __init__(self):
		self.digraph = Digraph()
		
		# A map of human readable labels to help users
		self.nodemap = {} # {'label' : <node object> }
		
		# A dictionary of nodes evaluated data. Values will be None if node needs evaluating
		self.cache = {} # {'label' : {data} }
		
		# A dictionary of errors associated with each node
		self.errors = {} # {'label' : [ "errors"]}
		
		# a set of updated nodes since last DAG evaluation
		self.updated = set() # ['labels']
	
	def write(self, location):
		import pickle
		fp = open(location, 'wb')
		pickle.dump(self, fp, protocol=2)
		fp.close()
		
	def get_dot(self):
		'Return string of dotgraph representation'
		d = pygraph.readwrite.dot.write(self.digraph)
		return d
	
	def write_dot(self, filename):
		dot = self.get_dot()
		fh = open(filename, 'w')
		fh.write(dot)
		fh.close()

		
		
	def get_dic(self):
		'Return a dictionary representation of digraph'
		st = breadth_first_search(self.digraph)[0]
		return st
	
	def get_edges(self):
		edges = self.digraph.edges()
		return edges
	
	def add_node(self, node, label):
		'Add a node to the DAG'
		
		# Check for node instance
		if not isinstance(node, Node):
			raise Exception("Provided node must be of type 'Node'")
		
		# Check if node object already in graph
		if self.nodemap.has_key(label):
			if node == self.nodemap[label]:
				# No need to re-enter the same object
				return
			else:
				raise Exception("Label '%s' already in use" % label)
			
		elif node in self.nodemap.values():
				raise Exception("Node already exists in DAG with label '%s'" % self.get_label_from_node(node))
		
		# Register DAG for node notifications
		node.register(self)
		
		# Add node to graph
		self.nodemap[label] = node
		self.cache[label] = None
		self.errors[label] = []
		self.digraph.add_node(label)
		
		
		
	def del_node(self, label):
		'Remove a node from the DAG by label'
		if not isinstance(label, str):
			raise Exception("Provided label not of type 'str'")
		
		if self.nodemap.has_key(label):
			node = self.nodemap[label]
			
			# Unregister DAG from node
			node.unregister(self)
			
			# Remove node
			self.digraph.del_node(label)
			del self.nodemap[label]
			
			if self.cache.has_key(label):
				del self.cache[label]
			if self.errors.has_key(label):
				del self.errors[label]
			if label in self.updated:
				self.updated.remove(label) 
		
		else:
			raise Exception("Provided label not found in DAG")
	
		
		
	def add_edge(self, parent_label, child_label):
		'Add an edge to DAG between parent_label and child_label'
		
		if not self.nodemap.has_key(parent_label):
			raise Exception("Provided parent label not found in DAG")
		elif not self.nodemap.has_key(child_label):
			raise Exception("Provided child label not found in DAG")
				
		
		edge = (parent_label, child_label)
		self.digraph.add_edge(edge)
		
		
		
	def del_edge(self, parent_label, child_label):
		'Remove an edge from DAG between parent_label and child_label'
		
		if not self.nodemap.has_key(parent_label):
			raise Exception("Provided parent label not found in DAG")
		elif not self.nodemap.has_key(child_label):
			raise Exception("Provided child label not found in DAG")
		
		edge = (parent_label, child_label)
		self.digraph.del_edge(edge)
		
		
		
	def get_nodes_from_labels(self, labels):
		if isinstance(labels, list):
			newlist = []
			for label in labels:
				node = self.get_node_from_label(label)
				newlist.append(node)
		elif isinstance(labels, str):
			node = self.get_node_from_label(label)
			return node
				
	
	def evaluate_dag(self):
		'Force every node to be evaluated'
		# First invalidate all changed caches
		self._invalidate_caches()
		
		# Then call update on every node
		sg = topological_sorting(self.digraph) # sorted graph
		for label in sg:
			self._update_node(label)

			
		
	def evaluate_node(self, label):
		'Return the correct data for a node after evaluation'
		
		if isinstance(label, Node):
			label = self.get_label_from_node(label)
		elif not isinstance(label, str):
			raise Exception("Provided label not of type 'str' or 'Node'")
		
		if not self.nodemap.has_key(label):
			raise Exception("Could not find node with label '%s' in DAG" % label)
		
		# first we invalidate the caches for nodes that have changed
		self._invalidate_caches()
		
		# then update the node
		self._update_node(label)
		
		# 
		if self.cache[label] == None:
			errors = self.errors[label]
			plural = ""
			if len(errors) > 1:
				plural = "s"
			msg = "Node '%s' could not be evaluated due to the following error%s:\n" % (label, plural)
			for error in errors:
				msg += "\t - %s\n" % error 
			raise Exception(msg)
		else:
			return self.cache[label]
		
	
	
	def mark_updated(self, node):
		'Method to add a node to the invalidated list - so it can be checked on next execution'
		label = self.get_label_from_node(node)
		self.updated.add(label)
		
	
	
	def _invalidate_caches(self):
		'invalidate the downstream caches of updated nodes'
		
		if len(self.updated) == 0:
			return
			
		# Sort the nodes in worklist and remove duplicates
		sg = topological_sorting(self.digraph) # sorted graph
		
		worklist = []
		# insert nodes into worklist in sorted order
		for node in sg:
			if node in self.updated:
				worklist.append(node)
		self.updated.clear()
		
		# iterate through worklist
		while worklist:
			node = worklist.pop() # one item at a time
			downstream = breadth_first_search(self.digraph, root=node)[1] # get all downstream labels
			for n in downstream:
				if n in worklist:
					# remove labels that will already be done
					worklist.remove(n)
				# remove cache entries
				self.cache[n] = None
				
		
			
	def _update_node(self, label, rgraph=None):
		'Recursively updates nodes up the graph'
		
		# If cache already exists, this node is ok
		if self.cache[label] != None:
			return
		
		# reverse digraph then get parents
		if rgraph is None:
			rgraph = self.digraph.reverse()
		parents = rgraph.neighbors(label)
		
		# recursively evaluate all parent nodes to check if updates are required
		for i in parents:
			self._update_node(i, rgraph)
		
		# If we are here, cache needs to be generated
		
		# default state of cache entry is None
		self.cache[label] = None
		self.errors[label] = []
		
		# temp variables for final node attributes
		errors = []
		cache = {}
		
		node = self.get_node_from_label(label)
		
		while parents:
			node1 = parents.pop() # Compare one parent node against all others at a time
			if parents == []:
				# merge evaluated node1 if nothing to compare with
				c = self.cache[node1]
				if c == None:
					errors.append("Node '%s' could not be evaluated due to errors in upstream node '%s'" % (label, node1) )
					# break because the error is upstream
					break
				cache.update(c)
			else:# clear the update
				for node2 in parents:
					# get merge and conflict info
					lhs = self.cache[node1]
					if lhs == None:
						errors.append("Node '%s' could not be evaluated due to errors in upstream node '%s'" % (label, node1) )
						# break because the error is upstream
						break
					rhs = self.cache[node2]
					if rhs == None:
						errors.append("Node '%s' could not be evaluated due to errors in upstream node '%s'" % (label, node2) )
						# break because the error is upstream
						break
					merge, conflicts = self._dict_merge(lhs, rhs)
					for key in conflicts:
						# Ignore conflict if it would be overwritten anyway
						if key in node._data.keys():
							pass
						else:
							errors.append("Key conflict between '%s' and '%s' for key '%s'" % (node1, node2, key))
							# Dont break, because we can list all that is wront with this node by continuing
					# update the cache with the merge
					cache.update(merge)
					
		# finally update the cache dic with the node data
		cache.update(node._data)
		
		# If there were any erros, append them to the object
		self.errors[label] = errors
		
		# write the cache entry
		if self.errors[label] == []:
			self.cache[label] = deepcopy(cache)		
		


	def _dict_merge(self, lhs, rhs):
		'Merge two nodes data, returning the merged data and any conflicting keys'		
		merge = {} # merged dics
		conflicts = [] # list of conflicting keys
		
		for key in lhs.keys():
			# auto-merge for missing key on right-hand-side.
			if (not rhs.has_key(key)):
				merge[key] = lhs[key]
			# on collision, raise exception
			elif (lhs[key] != rhs[key]):
				conflicts.append(key)
		for key in rhs.keys():
			# auto-merge for missing key on left-hand-side.
			if (not lhs.has_key(key)):
				merge[key] = rhs[key]
		return merge, conflicts
		
		
				
	def get_nodes(self):
		'Return all node objects in DAG'
		return self.nodemap.values()
	
	def get_labels(self):
		'Return labels for all nodes in DAG'
		return self.nodemap.keys()
	
	def get_dict(self):
		'Return a dictionary of {\'label\' : <node object>}'
		return self.nodemap
	
	def get_node_from_label(self, label):
		'Return the node for given label'
		if self.nodemap.has_key(label):
			return self.nodemap[label]
		else:
			raise Exception("Could not find node in DAG with label '%s'" % label)
		
	def get_label_from_node(self, node):
		'Return the label for given node'
		for k,v in self.nodemap.iteritems():
			if v == node:
				return k
		raise Exception("Could not find node in DAG")
		
		
		
	def show(self):
		'Simple method to generate a dotgraph and render it with graphviz'
		import gv
		import os
		import time
		
		dot = self.get_dot()
		# write the dotfile out for testing
		self.write_dot('/tmp/dag.dot')
		
		# Apply the dot layout to the graph
		gvo = gv.readstring(dot) # graphviz object
		gv.layout(gvo, 'dot')
		
		# render the layout into the node attributes
		gv.render(gvo)
		
		# write to a temp file and display in default viewer
		fileout = '/tmp/out.png'
		if os.path.exists(fileout):
			os.remove(fileout)
		gv.render(gvo, 'png', fileout)
		time.sleep(1)
		os.system('xdg-open %s 2> /dev/null' % fileout)