Raw File
graph.pyx
# distutils: language=c++

from cython.operator import dereference, preincrement

from .base import Algorithm
from .helpers import stdstring, pystring
from .traversal import Traversal
from . import graphio
import os

cdef class Graph:

	""" 
	Graph(n=0, weighted=False, directed=False, edgesIndexed=False)

	An undirected graph (with optional weights) and parallel iterator methods.

	Create a graph of `n` nodes. The graph has assignable edge weights if `weighted` is set to True.
	If `weighted` is set to False each edge has edge weight 1.0 and any other weight assignment will
	be ignored.

	Parameters
	----------
	n : int, optional
		Number of nodes.
	weighted : bool, optional
		If set to True, the graph can have edge weights other than 1.0. Default: False
	directed : bool, optional
		If set to True, the graph will be directed. Default: False
	edgesIndexed : bool, optional
		If set to True, the graph's edges will be indexed. Default: False
	"""

	def __cinit__(self, n=0, bool_t weighted=False, bool_t directed=False, bool_t edgesIndexed=False):
		if isinstance(n, Graph):
			self._this = move(_Graph((<Graph>n)._this, weighted, directed, edgesIndexed))
		else:
			self._this = move(_Graph(<count>n, weighted, directed, edgesIndexed))

	cdef setThis(self, _Graph& other):
		swap[_Graph](self._this, other)
		return self

	def __copy__(self):
		"""
		Generates a copy of the graph
		"""
		return Graph().setThis(_Graph(self._this))

	def __deepcopy__(self, memo):
		"""
		Generates a (deep) copy of the graph
		"""
		return Graph().setThis(_Graph(self._this))

	def __str__(self):
		return "NetworKit.Graph(n={0}, m={1})".format(self.numberOfNodes(), self.numberOfEdges())
	
	def __getstate__(self):
		return graphio.NetworkitBinaryWriter(graphio.Format.NetworkitBinary, chunks = 32, weightsType = 5).writeToBuffer(self)
	
	def __setstate__(self, state):
		newG = graphio.NetworkitBinaryReader().readFromBuffer(state)
		self._this = move(_Graph((<Graph>newG)._this, <bool_t>(newG.isWeighted()), <bool_t>(newG.isDirected()), <bool_t>(newG.hasEdgeIds())))

	def indexEdges(self, bool_t force = False):
		"""
		indexEdges(force = False)

		Assign integer ids to edges.

		Parameters
		----------
		force : bool, optional
			Force re-indexing of edges.
		"""
		self._this.indexEdges(force)

	def hasEdgeIds(self):
		"""
		hasEdgeIds()

		Returns true if edges have been indexed

		Returns
		-------
		bool
			If edges have been indexed
		"""
		return self._this.hasEdgeIds()

	def edgeId(self, node u, node v):
		"""
		edgeId(u, v)

		Returns
		-------
		int
			Id of the edge.
		"""
		return self._this.edgeId(u, v)

	def numberOfNodes(self):
		"""
		numberOfNodes()

		Get the number of nodes in the graph.

		Returns
		-------
		int
			The number of nodes.
		"""
		return self._this.numberOfNodes()

	def numberOfEdges(self):
		"""
		numberOfEdges()

		Get the number of edges in the graph.

		Returns
	 	-------
		int
			The number of edges.
		"""
		return self._this.numberOfEdges()

	def upperNodeIdBound(self):
		"""
		upperNodeIdBound()

		Get an upper bound for the node ids in the graph.

		Returns
		-------
		int
			An upper bound for the node ids in the graph.
		"""
		return self._this.upperNodeIdBound()

	def upperEdgeIdBound(self):
		"""
		upperEdgeIdBound()

		Get an upper bound for the edge ids in the graph.

		Returns
		-------
		int
			An upper bound for the edge ids in the graph.
		"""
		return self._this.upperEdgeIdBound()

	def degree(self, u):
		"""
		degree(u)

		Get the number of neighbors of `v`.

		Parameters
		----------
		v : int
			The input Node.

		Returns
		-------
		int
			The number of neighbors.
		"""
		return self._this.degree(u)

	def degreeIn(self, u):
		"""
		degreeIn(u)

		Get the number of in-neighbors of `v`.

		Parameters
		----------
		v : int
			The input Node.

		Returns
		-------
		int
			The number of in-neighbors.
		"""		
		return self._this.degreeIn(u)

	def degreeOut(self, u):
		"""
		degreeOut(u)

		Get the number of out-neighbors of `v`.

		Parameters
		----------
		v : int
			The Input Node.i
		Returns
		-------
		int
			The number of out-neighbors.
		"""
		return self._this.degreeOut(u)

	def weightedDegree(self, u, countSelfLoopsTwice=False):
		"""
		weightedDegree(u, countSelfLoopsTwice=False)

		Returns the weighted out-degree of u.

		For directed graphs this is the sum of weights of all outgoing edges of u.

		Parameters
		----------
		u : int
			The input Node.
		countSelfLoopsTwice : bool, optional
			If set to True, self-loops will be counted twice. Default: False

		Returns
		-------
		float
			The weighted out-degree of u.
		"""
		return self._this.weightedDegree(u, countSelfLoopsTwice)

	def weightedDegreeIn(self, u, countSelfLoopsTwice=False):
		"""
		weightedDegreeIn(u, countSelfLoopsTwice=False)

		Returns the weighted in-degree of u.

		For directed graphs this is the sum of weights of all ingoing edges of u.

		Parameters
		----------
		u : int
			The input node.
		countSelfLoopsTwice : bool, optional
			If set to True, self-loops will be counted twice. Default: False

		Returns
		-------
		float
			The weighted in-degree of u.
		"""
		return self._this.weightedDegreeIn(u, countSelfLoopsTwice)

	def isIsolated(self, u):
		"""
		isIsolated(u)

		If the node `u` is isolated.

		Parameters
		----------
		u : int
			The input node.

		Returns
		-------
		bool
			Indicates whether the node is isolated.
		"""
		return self._this.isIsolated(u)

	def addNode(self):
		""" 
		addNode()
		
		Add a new node to the graph and return it.

		Returns
		-------
		int
			The new node.
		"""
		return self._this.addNode()

	def addNodes(self, numberOfNewNodes):
		""" 
		addNodes(numberOfNewNodes)
		
		Add numberOfNewNodes many new nodes to the graph and return
		the id of the last node added.

		Parameters
		----------
		numberOfNewNodes : int
			Number of nodes to be added.

		Returns
		-------
		int
			The id of the last node added.
		"""
		assert(numberOfNewNodes >= 0)
		return self._this.addNodes(numberOfNewNodes)

	def removeNode(self, u):
		"""
		removeNode(u)
		
		Remove a node `u` and all incident edges from the graph.

		Incoming as well as outgoing edges will be removed.

		Parameters
		----------
		u : int
			Id of node to be removed.
		"""
		self._this.removeNode(u)

	def restoreNode(self, u):
		""" 
		restoreNode(u)

		Restores a previously deleted node `u` with its previous id in the graph.

		Parameters
		----------
		u : int
			The input node.
		"""
		self._this.restoreNode(u)

	def hasNode(self, u):
		""" 
		hasNode(u)
		
		Checks if the Graph has the node `u`, i.e. if `u` hasn't been deleted and is in the range of valid ids.

		Parameters
		----------
		u : int
			Id of node queried.

		Returns
		-------
		bool
			Indicates whether node `u` is part of the graph.
		"""
		return self._this.hasNode(u)

	def addEdge(self, u, v, w=1.0, addMissing = False):
		""" 
		addEdge(u, v, w=1.0, addMissing=False)
		
		Insert an undirected edge between the nodes `u` and `v`. If the graph is weighted you can optionally set a weight for this edge. 
		The default weight is 1.0. If one or both end-points do not exists and addMissing is set, they are silently added.
		
		Note
		----
		It is not checked whether this edge already exists, thus it is possible to create multi-edges.

	 	Parameters
	 	----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.
		w : float, optional
			Edge weight.
		addMissing : bool, optional
			Add missing endpoints if necessary (i.e., increase numberOfNodes). Default: False
		"""
		if not (self._this.hasNode(u) and self._this.hasNode(v)):
			if not addMissing:
				raise RuntimeError("Cannot create edge ({0}, {1}) as at least one end point does not exist".format(u,v))

			k = max(u, v)
			if k >= self._this.upperNodeIdBound():
				self._this.addNodes(k - self._this.upperNodeIdBound() + 1)

			if not self._this.hasNode(u):
				self._this.restoreNode(u)

			if not self._this.hasNode(v):
				self._this.restoreNode(v)

		self._this.addEdge(u, v, w)
		return self

	def setWeight(self, u, v, w):
		""" 
		setWeight(u, v, w)
		
		Set the weight of an edge. If the edge does not exist, it will be inserted.

		Parameters
		----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.
		w : float
			Edge weight.
		"""
		self._this.setWeight(u, v, w)
		return self

	def increaseWeight(self, u, v, w):
		""" 
		increaseWeight(u, v, w)
		
		Increase the weight of an edge. If the edge does not exist, it will be inserted.

		Parameters
		----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.
		w : float
			Edge weight.
		"""
		self._this.increaseWeight(u, v, w)
		return self

	def removeEdge(self, u, v):
		""" 
		removeEdge(u, v)
		
		Removes the undirected edge {`u`,`v`}.

		Parameters
		----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.
		"""
		self._this.removeEdge(u, v)
		return self

	def removeAllEdges(self):
		"""
		removeAllEdges()

		Removes all the edges in the graph.
		"""
		self._this.removeAllEdges()

	def removeSelfLoops(self):
		"""
		removeSelfLoops()
		
		Removes all self-loops from the graph.
		"""
		self._this.removeSelfLoops()

	def removeMultiEdges(self):
		""" Removes all multi-edges from the graph.
		"""
		self._this.removeMultiEdges()

	def swapEdge(self, node s1, node t1, node s2, node t2):
		"""
		swapEdge(s1, t1, s2, t2)

		Changes the edge (s1, t1) into (s1, t2) and the edge (s2, t2) into (s2, t1).

		If there are edge weights or edge ids, they are preserved. 
		
		Note
		----
		No check is performed if the swap is actually possible, i.e. does not generate duplicate edges.

		Parameters
		----------
		s1 : int
			Source node of the first edge.
		t1 : int
			Target node of the first edge.
		s2 : int
			Source node of the second edge.
		t2 : int
			Target node of the second edge.
		"""
		self._this.swapEdge(s1, t1, s2, t2)
		return self

	def compactEdges(self):
		"""
		compactEdges()
		
		Compact the edge storage, this should be called after executing many edge deletions.
		"""
		self._this.compactEdges()

	def sortEdges(self):
		"""
		sortEdges()

		Sorts the adjacency arrays by node id. While the running time is linear this
		temporarily duplicates the memory.
		"""
		self._this.sortEdges()

	def hasEdge(self, u, v):
		"""
		hasEdge(u, v) 
		
		Checks if undirected edge {`u`,`v`} exists in the graph.

		Parameters
		----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.

		Returns
		-------
		bool
			True if the edge exists, False otherwise.
		"""
		return self._this.hasEdge(u, v)

	def weight(self, u, v):
		""" 
		weight(u, v)

		Get edge weight of edge {`u` , `v`}. Returns 0 if edge does not exist.

		Parameters
		----------
		u : int
			Endpoint of edge.
		v : int
			Endpoint of edge.

		Returns
		-------
		float
			Edge weight of edge {`u` , `v`} or 0 if edge does not exist.
		"""
		return self._this.weight(u, v)

	def forNodes(self, object callback):
		""" 
		forNodes(callback)
		
		Experimental node iterator interface

		Parameters
		----------
		callback : object
			Any callable object that takes the parameter node.
		"""
		cdef NodeCallbackWrapper* wrapper
		try:
			wrapper = new NodeCallbackWrapper(callback)
			self._this.forNodes[NodeCallbackWrapper](dereference(wrapper))
		finally:
			del wrapper

	def forNodesInRandomOrder(self, object callback):
		""" 
		forNodesInRandomOrder(callback)
		
		Experimental node iterator interface

		Parameters:
		-----------
		callback : object
			Any callable object that takes the parameter node.
		"""
		cdef NodeCallbackWrapper* wrapper
		try:
			wrapper = new NodeCallbackWrapper(callback)
			self._this.forNodesInRandomOrder[NodeCallbackWrapper](dereference(wrapper))
		finally:
			del wrapper

	def forNodePairs(self, object callback):
		""" 
		forNodePairs(callback)
		
		Experimental node pair iterator interface

		Parameters
		----------
		callback : object
			Any callable object that takes the parameters tuple(int, int).
			Parameter list refering to (node id, node id).
		"""
		cdef NodePairCallbackWrapper* wrapper
		try:
			wrapper = new NodePairCallbackWrapper(callback)
			self._this.forNodePairs[NodePairCallbackWrapper](dereference(wrapper))
		finally:
			del wrapper

	def forEdges(self, object callback):
		""" 
		forEdges(callback)

		Experimental edge iterator interface

		Parameters
		----------
		callback : object
			Any callable object that takes the parameter tuple(int, int, float, int). 
			Parameter list refering to (node id, node id, edge weight, edge id).
		"""
		cdef EdgeCallBackWrapper* wrapper
		try:
			wrapper = new EdgeCallBackWrapper(callback)
			self._this.forEdges[EdgeCallBackWrapper](dereference(wrapper))
		finally:
			del wrapper

	def forEdgesOf(self, node u, object callback):
		""" 
		forEdgesOf(u, callback)
		
		Experimental incident (outgoing) edge iterator interface

		Parameters
		----------
		u : int
			The node of which incident edges shall be passed to the callback
		callback : object
			Any callable object that takes the parameter tuple(int, int, float, int).
			Parameter list refering to (node id, node id, edge weight, edge id).
		"""
		cdef EdgeCallBackWrapper* wrapper
		try:
			wrapper = new EdgeCallBackWrapper(callback)
			self._this.forEdgesOf[EdgeCallBackWrapper](u, dereference(wrapper))
		finally:
			del wrapper

	def forInEdgesOf(self, node u, object callback):
		""" 
		forInEdgesOf(u, callback)
		
		Experimental incident edge iterator interface

		Parameters
		----------
		u : int
			The node of which incident edges shall be passed to the callback
		callback : object
			Any callable object that takes the parameter tuple(int, int, float, int).
			Parameter list refering to (node id, node id, edge weight, edge id).
		"""
		cdef EdgeCallBackWrapper* wrapper
		try:
			wrapper = new EdgeCallBackWrapper(callback)
			self._this.forInEdgesOf[EdgeCallBackWrapper](u, dereference(wrapper))
		finally:
			del wrapper

	def isWeighted(self):
		"""
		isWeighted()

		Returns whether a graph is weighted.

		Returns
		-------
		bool
			True if this graph supports edge weights other than 1.0.
		"""
		return self._this.isWeighted()

	def isDirected(self):
		"""
		isDirected()
		
		Returns whether a graph is directed.
		
		Returns
		-------
		bool
			True if graph is directed.
		"""
		return self._this.isDirected()

	def totalEdgeWeight(self):
		""" 
		totalEdgeWeight()
		
		Get the sum of all edge weights.

		Returns
		-------
		float
			The sum of all edge weights.
		"""
		return self._this.totalEdgeWeight()

	def numberOfSelfLoops(self):
		"""
		numberOfSelfLoops()
		
		Get number of self-loops, i.e. edges {v, v}.

		Returns
		-------
		int
			Number of self-loops.
		"""
		return self._this.numberOfSelfLoops()

	def checkConsistency(self):
		"""
		checkConsistency()

		Check for invalid graph states, such as multi-edges.

		Returns
		-------
		bool
			True if graph contains invalid graph states.
		"""
		return self._this.checkConsistency()

	def iterNodes(self):
		"""
		iterNodes()

		Iterates over the nodes of the graph.
		"""
		it = self._this.nodeRange().begin()
		while it != self._this.nodeRange().end():
			yield dereference(it)
			preincrement(it)

	def iterEdges(self):
		"""
		iterEdges()

		Iterates over the edges of the graph.
		
		For each node u in the graph in ascending node id order,
		the iterator yields the out-edges of u in directed graphs
		and the edges (u,v) in which u < v for undirected graphs.
		
		It does not follow the order of edge ids (if present).
		"""
		it = self._this.edgeRange().begin()
		while it != self._this.edgeRange().end():
			yield dereference(it).u, dereference(it).v
			preincrement(it)

	def iterEdgesWeights(self):
		"""
		iterEdgeWeights()

		Iterates over the edges of the graph and their weights.
		"""
		it = self._this.edgeWeightRange().begin()
		while it != self._this.edgeWeightRange().end():
			yield dereference(it).u, dereference(it).v, dereference(it).weight
			preincrement(it)

	def iterNeighbors(self, u):
		"""
		iterNeighbors(u)

		Iterates over a range of the neighbors of a node.

		Parameters
		----------
		u : int
			The input node.
		"""
		it = self._this.neighborRange(u).begin()
		while it != self._this.neighborRange(u).end():
			yield dereference(it)
			preincrement(it)

	def iterInNeighbors(self, u):
		"""
		iterInNeighbors(u)

		Iterates over a range of the in-neighbors of a node.

		Parameters
		----------
		u : int
			The input node.
		"""
		it = self._this.inNeighborRange(u).begin()
		while it != self._this.inNeighborRange(u).end():
			yield dereference(it)
			preincrement(it)

	def iterNeighborsWeights(self, u):
		"""
		iterNeighborsWeights(u)

		Iterates over a range of the neighbors of a node including the edge weights.
		The iterator is not safe to use with unweighted graphs. To avoid unsafe behavior
		a runtime error will be thrown.

		Parameters
		----------
		u : int
			The input node.
		"""
		if not self._this.isWeighted():
			raise RuntimeError("iterNeighborsWeights: Use this iterator only on weighted graphs.")
		
		it = self._this.weightNeighborRange(u).begin()
		while it != self._this.weightNeighborRange(u).end():
			yield dereference(it)
			preincrement(it)		

	def iterInNeighborsWeights(self, u):
		"""
		iterInNeighborsWeights(u)

		Iterates over a range of the in-neighbors of a node including the edge weights.
		The iterator is not safe to use with unweighted graphs. To avoid unsafe behavior
		a runtime error will be thrown.

		Parameters
		----------
		u : int
			The input node.
		"""
		if not self._this.isWeighted():
			raise RuntimeError("iterInNeighborsWeights: Use this iterator only on weighted graphs.")

		it = self._this.weightInNeighborRange(u).begin()
		while it != self._this.weightInNeighborRange(u).end():
			yield dereference(it)
			preincrement(it)

	def attachNodeAttribute(self, name, ofType):
		"""
		attachNodeAttribute(name, ofType)

		Attaches a node attribute to the graph and returns it.

		.. code-block::
			
			A = G.attachNodeAttribute("attributeIdentifier", ofType)
		
		All values are initially undefined for existing nodes values can be set/get
		by 
		
		.. code-block:: 
		
			A[node] = value # set
			value = A[node] # get

		Getting undefined values raises a ValueError removing a node makes all
		its attributes undefined

		Notes
		-----
		Using node attributes is in experimental state. The API may change in future updates.

		Parameters
		----------
		name   : str
			Name for this attribute
		ofType : type
			Type of the attribute (either int, float, or str)

		Returns
		-------
		networkit.graph.NodeAttribute
			The resulting node attribute container.
		"""
		if not isinstance(name, str):
			raise Exception("Attribute name has to be a string")

		if ofType == int:
			return NodeAttribute(NodeIntAttribute().setThis(self._this.attachNodeIntAttribute(stdstring(name)), &self._this), int)
		elif ofType == float:
			return NodeAttribute(NodeDoubleAttribute().setThis(self._this.attachNodeDoubleAttribute(stdstring(name)), &self._this), float)
		elif ofType == str:
			return NodeAttribute(NodeStringAttribute().setThis(self._this.attachNodeStringAttribute(stdstring(name)), &self._this), str)

	def detachNodeAttribute(self, name):
		"""
		detachNodeAttribute(name)

		Detaches a node attribute from the graph.

		Notes
		-----
		Using node attributes is in experimental state. The API may change in future updates.

		Parameters
		----------
		name : str
			The distinguished name for the attribute to detach.
		"""
		if not isinstance(name, str):
			raise Exception("Attribute name has to be a string")
		self._this.detachNodeAttribute(stdstring(name))

# The following 3 classes NodeIntAttribute, NodeDoubleAttribute and 
# NodeStringAttribute are helper classes which cannot be generalized because
# they map to different C++ classes even if these are generated from the same
# C++ template - this results in some unpleasant code duplication.
# The generic (pure python) wrapper class for the user is NodeAttribute

cdef class NodeIntAttribute:

	cdef setThis(self, _NodeIntAttribute& other, _Graph* G):
		self._this.swap(other)
		self._G = G
		return self

	def __getitem__(self, node):
		try:
			value = self._this.get(node)
		except Exception as e:
			raise ValueError(str(e))
		return value

	def __setitem__(self, node, value):
		try:
			self._this.set(node, value)
		except Exception as e:
			raise ValueError(str(e))

	def __iter__(self):
		try:
			self._iter = self._this.begin()
		except Exception as e:
			raise ValueError(str(e))

		self._stopiter = self._this.end()
		return self

	def __next__(self):
		if self._iter == self._stopiter:
			raise StopIteration()
		val = dereference(self._iter)
		preincrement(self._iter)
		return val


cdef class NodeDoubleAttribute:
	cdef setThis(self, _NodeDoubleAttribute& other, _Graph* G):
		self._this.swap(other)
		self._G = G
		return self

	def __getitem__(self, node):
		try:
			value = self._this.get(node)
		except Exception as e:
			raise ValueError(str(e))
		return value

	def __setitem__(self, node, value):
		try:
			self._this.set(node, value)
		except Exception as e:
			raise ValueError(str(e))

	def __iter__(self):
		try:
			self._iter = self._this.begin()
		except Exception as e:
			raise ValueError(str(e))
		self._stopiter = self._this.end()
		return self

	def __next__(self):
		if self._iter == self._stopiter:
			raise StopIteration()
		val = dereference(self._iter)
		preincrement(self._iter)
		return val

cdef class NodeStringAttribute:

	cdef setThis(self, _NodeStringAttribute& other, _Graph* G):
		self._this.swap(other)
		self._G = G
		return self

	def __getitem__(self, node):
		try:
			value = pystring(self._this.get(node))
		except Exception as e:
			raise ValueError(str(e))
		return value

	def __setitem__(self, node, value):
		try:
			self._this.set(node, stdstring(value))
		except Exception as e:
			raise ValueError(str(e))

	def __iter__(self):
		try:
			self._iter = self._this.begin()
		except Exception as e:
			raise ValueError(str(e))
		self._stopiter = self._this.end()
		return self

	def __next__(self):
		if self._iter == self._stopiter:
			raise StopIteration()
		val = dereference(self._iter)
		val = (val[0], pystring(val[1]))
		preincrement(self._iter)
		return val

class NodeAttribute:
	"""
	Generic class for node attributes returned by networkit.graph.attachNodeAttribute().
	Example of attaching an int attribute to a graph g:

	.. code-block::

		att = g.attachNodeAttribute("name", int)`

	Set/get attributes of a single node 'u' with the [] operator:

	.. code-block::

		att[u] = 0
		att_val = att[u] # 'att_val' is 0

	Iterate over all the values of an attribute:

	.. code-block::

		for u, val in att:
			# The attribute value of node `u` is `val`.

	Notes
	-----
	Using node attributes is in experimental state. The API may change in future updates.
	"""

	def __init__(self, typedNodeAttribute, type):
		self.attr = typedNodeAttribute
		self.type = type

	def __getitem__(self, node):
		return self.attr[node]

	def __setitem__(self, index, value):
		if not isinstance(value, self.type):
			raise Exception("Wrong Attribute type")
		self.attr[index] = value

	def __iter__(self):
		self._iter = iter(self.attr)
		return self

	def __next__(self):
		return next(self._iter)

cdef cppclass EdgeCallBackWrapper:
	void* callback
	__init__(object callback):
		this.callback = <void*>callback
	void cython_call_operator(node u, node v, edgeweight w, edgeid eid):
		cdef bool_t error = False
		cdef string message
		try:
			(<object>callback)(u, v, w, eid)
		except Exception as e:
			error = True
			message = stdstring("An Exception occurred, aborting execution of iterator: {0}".format(e))
		if (error):
			throw_runtime_error(message)

cdef cppclass NodeCallbackWrapper:
	void* callback
	__init__(object callback):
		this.callback = <void*>callback
	void cython_call_operator(node u):
		cdef bool_t error = False
		cdef string message
		try:
			(<object>callback)(u)
		except Exception as e:
			error = True
			message = stdstring("An Exception occurred, aborting execution of iterator: {0}".format(e))
		if (error):
			throw_runtime_error(message)

cdef cppclass NodeDistCallbackWrapper:
	void* callback
	__init__(object callback):
		this.callback = <void*>callback
	void cython_call_operator(node u, count dist):
		cdef bool_t error = False
		cdef string message
		try:
			(<object>callback)(u, dist)
		except Exception as e:
			error = True
			message = stdstring("An Exception occurred, aborting execution of iterator: {0}".format(e))
		if (error):
			throw_runtime_error(message)

cdef cppclass NodePairCallbackWrapper:
	void* callback
	__init__(object callback):
		this.callback = <void*>callback
	void cython_call_operator(node u, node v):
		cdef bool_t error = False
		cdef string message
		try:
			(<object>callback)(u, v)
		except Exception as e:
			error = True
			message = stdstring("An Exception occurred, aborting execution of iterator: {0}".format(e))
		if (error):
			throw_runtime_error(message)

cdef class SpanningForest:
	""" 
	SpanningForest(G, nodes)
	
	Generates a spanning forest for a given graph

	Parameters
	----------
	G : networkit.Graph
		The input graph.
	nodes : list(int)
		A subset of nodes of `G` which induce the subgraph.
	"""
	cdef _SpanningForest* _this
	cdef Graph _G

	def __cinit__(self, Graph G not None):
		self._G = G
		self._this = new _SpanningForest(G._this)


	def __dealloc__(self):
		del self._this

	def run(self):
		"""
		run()

		Executes the algorithm.
		"""
		self._this.run()
		return self

	def getForest(self):
		"""
		getForest()

		Returns the spanning forest.

		Returns
		-------
		networkit.Graph
			The computed spanning forest.
		"""
		return Graph().setThis(self._this.getForest())

cdef class RandomMaximumSpanningForest(Algorithm):
	"""
	RandomMaximumSpanningForest(G, attributes)

	Computes a random maximum-weight spanning forest using Kruskal's algorithm by randomizing the order of edges of the same weight.

	Parameters
	----------
	G : networkit.Graph
		The input graph.
	attribute : list(int) or list(float)
		If given, this edge attribute is used instead of the edge weights.
	"""

	def __cinit__(self, Graph G not None, vector[double] attribute = vector[double]()):
		self._G = G
		if attribute.empty():
			self._this = new _RandomMaximumSpanningForest(G._this)
		else:
			self._attribute = move(attribute)
			self._this = new _RandomMaximumSpanningForest(G._this, self._attribute)

	def getMSF(self, bool_t move):
		"""
		getMSF(move)

		Gets the calculated maximum-weight spanning forest as graph.

		Parameters
		----------
		move : bool
			If the graph shall be moved out of the algorithm instance.

		Returns
		-------
		networkit.Graph
			The calculated maximum-weight spanning forest.
		"""
		return Graph().setThis((<_RandomMaximumSpanningForest*>(self._this)).getMSF(move))

	def getAttribute(self, bool_t move = False):
		"""
		getAttribute(move=False)

		Get a bool attribute that indicates for each edge if it is part of the calculated maximum-weight spanning forest.
		This attribute is only calculated and can thus only be request if the supplied graph has edge ids.

		Parameters
		----------
		move : bool, optional
			If the attribute shall be moved out of the algorithm instance. Default: False

		Returns
		-------
		list(bool)
			The list with the bool attribute for each edge.
		"""
		return (<_RandomMaximumSpanningForest*>(self._this)).getAttribute(move)

	def inMSF(self, node u, node v = _none):
		"""
		inMSF(u, v = None)

		Checks if the edge (u, v) or the edge with id u is part of the calculated maximum-weight spanning forest.

		Parameters
		----------
		u : int
			The first node of the edge to check or the edge id of the edge to check.
		v : int, optional
			The second node of the edge to check (only if u is not an edge id). Default: None

		Returns
		-------
		bool
			If the edge is part of the calculated maximum-weight spanning forest.
		"""
		if v == _none:
			return (<_RandomMaximumSpanningForest*>(self._this)).inMSF(u)
		else:
			return (<_RandomMaximumSpanningForest*>(self._this)).inMSF(u, v)

cdef class UnionMaximumSpanningForest(Algorithm):
	"""
	UnionMaximumSpanningForest(G, attribute)

	Union maximum-weight spanning forest algorithm, computes the union of all maximum-weight spanning forests using Kruskal's algorithm.

	Parameters
	----------
	G : networkit.Graph
		The input graph.
	attribute : list(int) or list(float)
		If given, this edge attribute is used instead of the edge weights.
	"""

	def __cinit__(self, Graph G not None, vector[double] attribute = vector[double]()):
		self._G = G

		if attribute.empty():
			self._this = new _UnionMaximumSpanningForest(G._this)
		else:
			self._this = new _UnionMaximumSpanningForest(G._this, attribute)

	def getUMSF(self, bool_t move = False):
		"""
		getUMSF(move=False)

		Gets the union of all maximum-weight spanning forests as graph.

		Parameters
		----------
		move : bool, optional
			If the graph shall be moved out of the algorithm instance. Default: False

		Returns
		-------
		networkit.Graph
			The calculated union of all maximum-weight spanning forests.
		"""
		return Graph().setThis((<_UnionMaximumSpanningForest*>(self._this)).getUMSF(move))

	def getAttribute(self, bool_t move = False):
		"""
		getAttribute(move=False)
		
		Get a bool attribute that indicates for each edge if it is part of any maximum-weight spanning forest.

		This attribute is only calculated and can thus only be request if the supplied graph has edge ids.

		Parameters
		----------
		move : bool, optional
			If the attribute shall be moved out of the algorithm instance. Default: False

		Returns
		-------
		list(bool)
			The list with the bool attribute for each edge.
		"""
		return (<_UnionMaximumSpanningForest*>(self._this)).getAttribute(move)

	def inUMST(self, node u, node v = _none):
		"""
		inUMST(u, v=None)

		Checks if the edge (u, v) or the edge with id u is part of any maximum-weight spanning forest.

		Parameters
		----------
		u : int
			The first node of the edge to check or the edge id of the edge to check.
		v : int, optional
			The second node of the edge to check (only if u is not an edge id). Default: None

		Returns
		-------
		bool
			If the edge is part of any maximum-weight spanning forest.
		"""
		if v == _none:
			return (<_UnionMaximumSpanningForest*>(self._this)).inUMSF(u)
		else:
			return (<_UnionMaximumSpanningForest*>(self._this)).inUMSF(u, v)
back to top