pygel3d.graph

This module provides a Graph class and functionality for skeletonization using graphs.

  1""" This module provides a Graph class and functionality for skeletonization using graphs. """
  2import ctypes as ct
  3import numpy as np
  4from pygel3d import hmesh, lib_py_gel, IntVector
  5
  6class Graph:
  7    """ This class is for representing graphs embedded in 3D. The class does not in
  8    itself come with many features: it contains methods for creating, accessing, and
  9    housekeeping. When vertices are used as parameters in the functions below, we usually
 10    use the parameter name n (for node). n is simply an index (i.e. an integer) that
 11    refers to a node (aka vertex)."""
 12    def __init__(self,orig=None):
 13        if orig == None:
 14            self.obj = lib_py_gel.Graph_new()
 15        else:
 16            self.obj = lib_py_gel.Graph_copy(orig.obj)
 17    def __del__(self):
 18        lib_py_gel.Graph_delete(self.obj)
 19    def clear(self):
 20        """ Clear the graph. """
 21        lib_py_gel.Graph_clear(self.obj)
 22    def cleanup(self):
 23        """ Cleanup reorders the graph nodes such that there is no
 24        gap in the index range. """
 25        lib_py_gel.Graph_cleanup(self.obj)
 26    def nodes(self):
 27        """ Get all nodes as an iterable range """
 28        nodes = IntVector()
 29        lib_py_gel.Graph_nodes(self.obj, nodes.obj)
 30        return nodes
 31    def neighbors(self, n, mode='n'):
 32        """ Get the neighbors of node n. The final argument is either 'n' or 'e'. If it is 'n'
 33        the function returns all neighboring nodes, and if it is 'e' it returns incident edges."""
 34        nbors = IntVector()
 35        lib_py_gel.Graph_neighbors(self.obj, n, nbors.obj, ct.c_char(mode.encode('ascii')))
 36        return nbors
 37    def positions(self):
 38        """ Get the vertex positions by reference. You can assign to the
 39        positions. """
 40        pos = ct.POINTER(ct.c_double)()
 41        n = lib_py_gel.Graph_positions(self.obj, ct.byref(pos))
 42        return np.ctypeslib.as_array(pos,(n,3))
 43    def average_edge_length(self):
 44        """ Returns the average edge length. """
 45        ael = lib_py_gel.Graph_average_edge_length(self.obj)
 46        return ael
 47    def add_node(self, p):
 48        """ Adds node with position p to the graph and returns the
 49        index of the new node. """
 50        return lib_py_gel.Graph_add_node(self.obj, np.array(p))
 51    def remove_node(self, n):
 52        """ Removes the node n passed as argument. This does not change
 53        any indices of other nodes, but n is then invalid. """
 54        lib_py_gel.Graph_remove_node(self.obj, n)
 55    def node_in_use(self, n):
 56        """ Checks if n is in_use. This function returns false both
 57        if n has been removed and if n is an index outside the range of
 58        indices that are used. """
 59        return lib_py_gel.Graph_node_in_use(self.obj, n)
 60    def connect_nodes(self, n0, n1):
 61        """ Creates a new edge connecting nodes n0 and n1. The index of
 62        the new edge is returned. """
 63        return lib_py_gel.Graph_connect_nodes(self.obj, n0, n1)
 64    def disconnect_nodes(self, n0, n1):
 65        """ Disconect nodes n0 and n1"""
 66        lib_py_gel.Graph_disconnect_nodes(self.obj, n0, n1)
 67    def merge_nodes(self, n0, n1, avg_pos):
 68        """ Merge nodes n0 and n1. avg_pos indicates if you want the position to be the average. """
 69        lib_py_gel.Graph_merge_nodes(self.obj, n0, n1, avg_pos)
 70
 71
 72def from_mesh(m):
 73    """ Creates a graph from a mesh. The argument, m, is the input mesh,
 74    and the function returns a graph with the same vertices and edges
 75    as m."""
 76    g = Graph()
 77    lib_py_gel.graph_from_mesh(m.obj, g.obj)
 78    return g
 79
 80def load(fn):
 81    """ Load a graph from a file. The argument, fn, is the filename which
 82    is in a special format similar to Wavefront obj. The loaded graph is
 83    returned by the function - or None if loading failed. """
 84    s = ct.c_char_p(fn.encode('utf-8'))
 85    g = Graph()
 86    if lib_py_gel.graph_load(g.obj, s):
 87        return g
 88    return None
 89
 90def save(fn, g):
 91    """ Save graph to a file. The first argument, fn, is the file name,
 92    and g is the graph. This function returns True if saving happened and
 93    False otherwise. """
 94    s = ct.c_char_p(fn.encode('utf-8'))
 95    return lib_py_gel.graph_save(g.obj, s)
 96
 97def to_mesh_cyl(g, fudge=0.0):
 98    """ Creates a Manifold mesh from the graph. The first argument, g, is the
 99    graph we want converted, and fudge is a constant that is used to increase the radius
100    of every node. This is useful if the radii are 0. """
101    m = hmesh.Manifold()
102    lib_py_gel.graph_to_mesh_cyl(g.obj, m.obj, fudge)
103    return m
104
105def to_mesh_iso(g, fudge=0.0, res=256):
106    """ Creates a Manifold mesh from the graph. The first argument, g, is the
107    graph we want converted, and fudge is a constant that is used to increase the radius
108    of every node. This is useful if the radii are 0. """
109    m = hmesh.Manifold()
110    lib_py_gel.graph_to_mesh_iso(g.obj, m.obj, fudge, res)
111    return m
112
113
114def smooth(g, iter=1, alpha=1.0):
115    """ Simple Laplacian smoothing of a graph. The first argument is the Graph, g, iter
116    is the number of iterations, and alpha is the weight. If the weight is high,
117    each iteration causes a lot of smoothing, and a high number of iterations
118    ensures that the effect of smoothing diffuses throughout the graph, i.e. that the
119    effect is more global than local. """
120    lib_py_gel.graph_smooth(g.obj, iter, alpha)
121
122def edge_contract(g, dist_thresh):
123    """ Simplifies a graph by contracting edges. The first argument, g, is the graph,
124    and only edges shorter than dist_thresh are contracted. When an edge is contracted
125    the merged vertices are moved to the average of their former positions. Thus,
126    the ordering in which contractions are carried out matters. Hence, edges are
127    contracted in the order of increasing length and edges are only considered if
128    neither end point is the result of a contraction, but the process is then repeated
129    until no more contractions are possible. Returns total number of contractions. """
130    return lib_py_gel.graph_edge_contract(g.obj, dist_thresh)
131
132def prune(g):
133    """ Prune leaves of a graph. The graph, g, is passed as the argument. This function
134        removes leaf nodes (valency 1) whose only neighbour has valency > 2. In practice
135        such isolated leaves are frequently spurious if the graph is a skeleton. Does not
136        return a value. """
137    lib_py_gel.graph_prune(g.obj)
138    
139def saturate(g, hops=2, dist_frac=1.001, rad=1e300):
140    """ Saturate the graph with edges. This is not a complete saturation. Edges are
141    introduced between a vertex and other vertices that are reachable in hops steps, i.e.
142    hops-order neighbors. dist_frac and rad are parameters used to govern the precise
143    behaviour. Two nodes are only connected if their distance is less than rad and if
144    their distance is less than dist_frac times the length of the path along existing
145    edges in the graph. If dist_frac is at approximately 1 and rad is enormous, these
146    two parameters make no difference. """
147    lib_py_gel.graph_saturate(g.obj, hops, dist_frac, rad)
148    
149def LS_skeleton(g, sampling=True):
150    """ Skeletonize a graph using the local separators approach. The first argument,
151        g, is the graph, and, sampling indicates whether we try to use all vertices
152        (False) as starting points for finding separators or just a sampling (True).
153        The function returns a new graph which is the skeleton of the input graph. """
154    skel = Graph()
155    mapping = IntVector()
156    lib_py_gel.graph_LS_skeleton(g.obj, skel.obj, mapping.obj, sampling)
157    return skel
158    
159def LS_skeleton_and_map(g, sampling=True):
160    """ Skeletonize a graph using the local separators approach. The first argument,
161        g, is the graph, and, sampling indicates whether we try to use all vertices
162        (False) as starting points for finding separators or just a sampling (True).
163        The function returns a tuple containing a new graph which is the skeleton of
164        the input graph and a map from the graph nodes to the skeletal nodes. """
165    skel = Graph()
166    mapping = IntVector()
167    lib_py_gel.graph_LS_skeleton(g.obj, skel.obj, mapping.obj, sampling)
168    return skel, mapping
169
170def MSLS_skeleton(g, grow_thresh=64):
171    """ Skeletonize a graph using the multi-scale local separators approach. The first argument,
172        g, is the graph, and, sampling indicates whether we try to use all vertices
173        (False) as starting points for finding separators or just a sampling (True).
174        The function returns a new graph which is the skeleton of the input graph. """
175    skel = Graph()
176    mapping = IntVector()
177    lib_py_gel.graph_MSLS_skeleton(g.obj, skel.obj, mapping.obj, grow_thresh)
178    return skel
179    
180def MSLS_skeleton_and_map(g, grow_thresh=64):
181    """ Skeletonize a graph using the multi-scale local separators approach. The first argument,
182        g, is the graph, and, sampling indicates whether we try to use all vertices
183        (False) as starting points for finding separators or just a sampling (True).
184        The function returns a tuple containing a new graph which is the skeleton of
185        the input graph and a map from the graph nodes to the skeletal nodes. """
186    skel = Graph()
187    mapping = IntVector()
188    lib_py_gel.graph_MSLS_skeleton(g.obj, skel.obj, mapping.obj, grow_thresh)
189    return skel, mapping
190
191
192def front_skeleton_and_map(g, colors, intervals=100):
193    """ Skeletonize a graph using the front separators approach. The first argument,
194        g, is the graph, and, colors is an nD array where each column contains a sequence
195        of floating point values - one for each node. We can have as many columns as needed
196        for the front separator computation. We can think of this as a coloring
197        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
198        of the nodes or some other function that indicates something about the structure of the
199        graph. The function returns a tuple containing a new graph which is the
200        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
201    skel = Graph()
202    mapping = IntVector()
203    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
204    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
205    print("N_col:", N_col)
206    pos = g.positions()
207    lib_py_gel.graph_front_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
208    return skel, mapping
209
210def front_skeleton(g, colors, intervals=100):
211    """ Skeletonize a graph using the front separators approach. The first argument,
212        g, is the graph, and, colors is a nD array where each column contains a sequence
213        of floating point values - one for each node. We can have as many columns as needed
214        for the front separator computation. We can think of this as a coloring
215        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
216        of the nodes or some other function that indicates something about the structure of the
217        graph. The function returns a tuple containing a new graph which is the
218        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
219    skel = Graph()
220    mapping = IntVector()
221    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
222    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
223    print("N_col:", N_col)
224    pos = g.positions()
225    lib_py_gel.graph_front_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
226    return skel
227
228def combined_skeleton_and_map(g, colors, intervals=100):
229    """ Skeletonize a graph using both the front separators approach and the multi scale local separators.
230        The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence
231        of floating point values - one for each node. We can have as many columns as needed
232        for the front separator computation. We can think of this as a coloring
233        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
234        of the nodes or some other function that indicates something about the structure of the
235        graph. The function returns a tuple containing a new graph which is the
236        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
237    skel = Graph()
238    mapping = IntVector()
239    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
240    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
241    print("N_col:", N_col)
242    pos = g.positions()
243    lib_py_gel.graph_combined_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
244    return skel, mapping
245
246def combined_skeleton(g, colors, intervals=100):
247    """ Skeletonize a graph using both the front separators approach and the multi scale local separators.
248        The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence
249        of floating point values - one for each node. We can have as many columns as needed
250        for the front separator computation. We can think of this as a coloring
251        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
252        of the nodes or some other function that indicates something about the structure of the
253        graph. The function returns a new graph which is the
254        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
255    skel = Graph()
256    mapping = IntVector()
257    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
258    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
259    print("N_col:", N_col)
260    pos = g.positions()
261    lib_py_gel.graph_combined_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
262    return skel
class Graph:
 7class Graph:
 8    """ This class is for representing graphs embedded in 3D. The class does not in
 9    itself come with many features: it contains methods for creating, accessing, and
10    housekeeping. When vertices are used as parameters in the functions below, we usually
11    use the parameter name n (for node). n is simply an index (i.e. an integer) that
12    refers to a node (aka vertex)."""
13    def __init__(self,orig=None):
14        if orig == None:
15            self.obj = lib_py_gel.Graph_new()
16        else:
17            self.obj = lib_py_gel.Graph_copy(orig.obj)
18    def __del__(self):
19        lib_py_gel.Graph_delete(self.obj)
20    def clear(self):
21        """ Clear the graph. """
22        lib_py_gel.Graph_clear(self.obj)
23    def cleanup(self):
24        """ Cleanup reorders the graph nodes such that there is no
25        gap in the index range. """
26        lib_py_gel.Graph_cleanup(self.obj)
27    def nodes(self):
28        """ Get all nodes as an iterable range """
29        nodes = IntVector()
30        lib_py_gel.Graph_nodes(self.obj, nodes.obj)
31        return nodes
32    def neighbors(self, n, mode='n'):
33        """ Get the neighbors of node n. The final argument is either 'n' or 'e'. If it is 'n'
34        the function returns all neighboring nodes, and if it is 'e' it returns incident edges."""
35        nbors = IntVector()
36        lib_py_gel.Graph_neighbors(self.obj, n, nbors.obj, ct.c_char(mode.encode('ascii')))
37        return nbors
38    def positions(self):
39        """ Get the vertex positions by reference. You can assign to the
40        positions. """
41        pos = ct.POINTER(ct.c_double)()
42        n = lib_py_gel.Graph_positions(self.obj, ct.byref(pos))
43        return np.ctypeslib.as_array(pos,(n,3))
44    def average_edge_length(self):
45        """ Returns the average edge length. """
46        ael = lib_py_gel.Graph_average_edge_length(self.obj)
47        return ael
48    def add_node(self, p):
49        """ Adds node with position p to the graph and returns the
50        index of the new node. """
51        return lib_py_gel.Graph_add_node(self.obj, np.array(p))
52    def remove_node(self, n):
53        """ Removes the node n passed as argument. This does not change
54        any indices of other nodes, but n is then invalid. """
55        lib_py_gel.Graph_remove_node(self.obj, n)
56    def node_in_use(self, n):
57        """ Checks if n is in_use. This function returns false both
58        if n has been removed and if n is an index outside the range of
59        indices that are used. """
60        return lib_py_gel.Graph_node_in_use(self.obj, n)
61    def connect_nodes(self, n0, n1):
62        """ Creates a new edge connecting nodes n0 and n1. The index of
63        the new edge is returned. """
64        return lib_py_gel.Graph_connect_nodes(self.obj, n0, n1)
65    def disconnect_nodes(self, n0, n1):
66        """ Disconect nodes n0 and n1"""
67        lib_py_gel.Graph_disconnect_nodes(self.obj, n0, n1)
68    def merge_nodes(self, n0, n1, avg_pos):
69        """ Merge nodes n0 and n1. avg_pos indicates if you want the position to be the average. """
70        lib_py_gel.Graph_merge_nodes(self.obj, n0, n1, avg_pos)

This class is for representing graphs embedded in 3D. The class does not in itself come with many features: it contains methods for creating, accessing, and housekeeping. When vertices are used as parameters in the functions below, we usually use the parameter name n (for node). n is simply an index (i.e. an integer) that refers to a node (aka vertex).

def clear(self):
20    def clear(self):
21        """ Clear the graph. """
22        lib_py_gel.Graph_clear(self.obj)

Clear the graph.

def cleanup(self):
23    def cleanup(self):
24        """ Cleanup reorders the graph nodes such that there is no
25        gap in the index range. """
26        lib_py_gel.Graph_cleanup(self.obj)

Cleanup reorders the graph nodes such that there is no gap in the index range.

def nodes(self):
27    def nodes(self):
28        """ Get all nodes as an iterable range """
29        nodes = IntVector()
30        lib_py_gel.Graph_nodes(self.obj, nodes.obj)
31        return nodes

Get all nodes as an iterable range

def neighbors(self, n, mode='n'):
32    def neighbors(self, n, mode='n'):
33        """ Get the neighbors of node n. The final argument is either 'n' or 'e'. If it is 'n'
34        the function returns all neighboring nodes, and if it is 'e' it returns incident edges."""
35        nbors = IntVector()
36        lib_py_gel.Graph_neighbors(self.obj, n, nbors.obj, ct.c_char(mode.encode('ascii')))
37        return nbors

Get the neighbors of node n. The final argument is either 'n' or 'e'. If it is 'n' the function returns all neighboring nodes, and if it is 'e' it returns incident edges.

def positions(self):
38    def positions(self):
39        """ Get the vertex positions by reference. You can assign to the
40        positions. """
41        pos = ct.POINTER(ct.c_double)()
42        n = lib_py_gel.Graph_positions(self.obj, ct.byref(pos))
43        return np.ctypeslib.as_array(pos,(n,3))

Get the vertex positions by reference. You can assign to the positions.

def average_edge_length(self):
44    def average_edge_length(self):
45        """ Returns the average edge length. """
46        ael = lib_py_gel.Graph_average_edge_length(self.obj)
47        return ael

Returns the average edge length.

def add_node(self, p):
48    def add_node(self, p):
49        """ Adds node with position p to the graph and returns the
50        index of the new node. """
51        return lib_py_gel.Graph_add_node(self.obj, np.array(p))

Adds node with position p to the graph and returns the index of the new node.

def remove_node(self, n):
52    def remove_node(self, n):
53        """ Removes the node n passed as argument. This does not change
54        any indices of other nodes, but n is then invalid. """
55        lib_py_gel.Graph_remove_node(self.obj, n)

Removes the node n passed as argument. This does not change any indices of other nodes, but n is then invalid.

def node_in_use(self, n):
56    def node_in_use(self, n):
57        """ Checks if n is in_use. This function returns false both
58        if n has been removed and if n is an index outside the range of
59        indices that are used. """
60        return lib_py_gel.Graph_node_in_use(self.obj, n)

Checks if n is in_use. This function returns false both if n has been removed and if n is an index outside the range of indices that are used.

def connect_nodes(self, n0, n1):
61    def connect_nodes(self, n0, n1):
62        """ Creates a new edge connecting nodes n0 and n1. The index of
63        the new edge is returned. """
64        return lib_py_gel.Graph_connect_nodes(self.obj, n0, n1)

Creates a new edge connecting nodes n0 and n1. The index of the new edge is returned.

def disconnect_nodes(self, n0, n1):
65    def disconnect_nodes(self, n0, n1):
66        """ Disconect nodes n0 and n1"""
67        lib_py_gel.Graph_disconnect_nodes(self.obj, n0, n1)

Disconect nodes n0 and n1

def merge_nodes(self, n0, n1, avg_pos):
68    def merge_nodes(self, n0, n1, avg_pos):
69        """ Merge nodes n0 and n1. avg_pos indicates if you want the position to be the average. """
70        lib_py_gel.Graph_merge_nodes(self.obj, n0, n1, avg_pos)

Merge nodes n0 and n1. avg_pos indicates if you want the position to be the average.

def from_mesh(m):
73def from_mesh(m):
74    """ Creates a graph from a mesh. The argument, m, is the input mesh,
75    and the function returns a graph with the same vertices and edges
76    as m."""
77    g = Graph()
78    lib_py_gel.graph_from_mesh(m.obj, g.obj)
79    return g

Creates a graph from a mesh. The argument, m, is the input mesh, and the function returns a graph with the same vertices and edges as m.

def load(fn):
81def load(fn):
82    """ Load a graph from a file. The argument, fn, is the filename which
83    is in a special format similar to Wavefront obj. The loaded graph is
84    returned by the function - or None if loading failed. """
85    s = ct.c_char_p(fn.encode('utf-8'))
86    g = Graph()
87    if lib_py_gel.graph_load(g.obj, s):
88        return g
89    return None

Load a graph from a file. The argument, fn, is the filename which is in a special format similar to Wavefront obj. The loaded graph is returned by the function - or None if loading failed.

def save(fn, g):
91def save(fn, g):
92    """ Save graph to a file. The first argument, fn, is the file name,
93    and g is the graph. This function returns True if saving happened and
94    False otherwise. """
95    s = ct.c_char_p(fn.encode('utf-8'))
96    return lib_py_gel.graph_save(g.obj, s)

Save graph to a file. The first argument, fn, is the file name, and g is the graph. This function returns True if saving happened and False otherwise.

def to_mesh_cyl(g, fudge=0.0):
 98def to_mesh_cyl(g, fudge=0.0):
 99    """ Creates a Manifold mesh from the graph. The first argument, g, is the
100    graph we want converted, and fudge is a constant that is used to increase the radius
101    of every node. This is useful if the radii are 0. """
102    m = hmesh.Manifold()
103    lib_py_gel.graph_to_mesh_cyl(g.obj, m.obj, fudge)
104    return m

Creates a Manifold mesh from the graph. The first argument, g, is the graph we want converted, and fudge is a constant that is used to increase the radius of every node. This is useful if the radii are 0.

def to_mesh_iso(g, fudge=0.0, res=256):
106def to_mesh_iso(g, fudge=0.0, res=256):
107    """ Creates a Manifold mesh from the graph. The first argument, g, is the
108    graph we want converted, and fudge is a constant that is used to increase the radius
109    of every node. This is useful if the radii are 0. """
110    m = hmesh.Manifold()
111    lib_py_gel.graph_to_mesh_iso(g.obj, m.obj, fudge, res)
112    return m

Creates a Manifold mesh from the graph. The first argument, g, is the graph we want converted, and fudge is a constant that is used to increase the radius of every node. This is useful if the radii are 0.

def smooth(g, iter=1, alpha=1.0):
115def smooth(g, iter=1, alpha=1.0):
116    """ Simple Laplacian smoothing of a graph. The first argument is the Graph, g, iter
117    is the number of iterations, and alpha is the weight. If the weight is high,
118    each iteration causes a lot of smoothing, and a high number of iterations
119    ensures that the effect of smoothing diffuses throughout the graph, i.e. that the
120    effect is more global than local. """
121    lib_py_gel.graph_smooth(g.obj, iter, alpha)

Simple Laplacian smoothing of a graph. The first argument is the Graph, g, iter is the number of iterations, and alpha is the weight. If the weight is high, each iteration causes a lot of smoothing, and a high number of iterations ensures that the effect of smoothing diffuses throughout the graph, i.e. that the effect is more global than local.

def edge_contract(g, dist_thresh):
123def edge_contract(g, dist_thresh):
124    """ Simplifies a graph by contracting edges. The first argument, g, is the graph,
125    and only edges shorter than dist_thresh are contracted. When an edge is contracted
126    the merged vertices are moved to the average of their former positions. Thus,
127    the ordering in which contractions are carried out matters. Hence, edges are
128    contracted in the order of increasing length and edges are only considered if
129    neither end point is the result of a contraction, but the process is then repeated
130    until no more contractions are possible. Returns total number of contractions. """
131    return lib_py_gel.graph_edge_contract(g.obj, dist_thresh)

Simplifies a graph by contracting edges. The first argument, g, is the graph, and only edges shorter than dist_thresh are contracted. When an edge is contracted the merged vertices are moved to the average of their former positions. Thus, the ordering in which contractions are carried out matters. Hence, edges are contracted in the order of increasing length and edges are only considered if neither end point is the result of a contraction, but the process is then repeated until no more contractions are possible. Returns total number of contractions.

def prune(g):
133def prune(g):
134    """ Prune leaves of a graph. The graph, g, is passed as the argument. This function
135        removes leaf nodes (valency 1) whose only neighbour has valency > 2. In practice
136        such isolated leaves are frequently spurious if the graph is a skeleton. Does not
137        return a value. """
138    lib_py_gel.graph_prune(g.obj)

Prune leaves of a graph. The graph, g, is passed as the argument. This function removes leaf nodes (valency 1) whose only neighbour has valency > 2. In practice such isolated leaves are frequently spurious if the graph is a skeleton. Does not return a value.

def saturate(g, hops=2, dist_frac=1.001, rad=1e+300):
140def saturate(g, hops=2, dist_frac=1.001, rad=1e300):
141    """ Saturate the graph with edges. This is not a complete saturation. Edges are
142    introduced between a vertex and other vertices that are reachable in hops steps, i.e.
143    hops-order neighbors. dist_frac and rad are parameters used to govern the precise
144    behaviour. Two nodes are only connected if their distance is less than rad and if
145    their distance is less than dist_frac times the length of the path along existing
146    edges in the graph. If dist_frac is at approximately 1 and rad is enormous, these
147    two parameters make no difference. """
148    lib_py_gel.graph_saturate(g.obj, hops, dist_frac, rad)

Saturate the graph with edges. This is not a complete saturation. Edges are introduced between a vertex and other vertices that are reachable in hops steps, i.e. hops-order neighbors. dist_frac and rad are parameters used to govern the precise behaviour. Two nodes are only connected if their distance is less than rad and if their distance is less than dist_frac times the length of the path along existing edges in the graph. If dist_frac is at approximately 1 and rad is enormous, these two parameters make no difference.

def LS_skeleton(g, sampling=True):
150def LS_skeleton(g, sampling=True):
151    """ Skeletonize a graph using the local separators approach. The first argument,
152        g, is the graph, and, sampling indicates whether we try to use all vertices
153        (False) as starting points for finding separators or just a sampling (True).
154        The function returns a new graph which is the skeleton of the input graph. """
155    skel = Graph()
156    mapping = IntVector()
157    lib_py_gel.graph_LS_skeleton(g.obj, skel.obj, mapping.obj, sampling)
158    return skel

Skeletonize a graph using the local separators approach. The first argument, g, is the graph, and, sampling indicates whether we try to use all vertices (False) as starting points for finding separators or just a sampling (True). The function returns a new graph which is the skeleton of the input graph.

def LS_skeleton_and_map(g, sampling=True):
160def LS_skeleton_and_map(g, sampling=True):
161    """ Skeletonize a graph using the local separators approach. The first argument,
162        g, is the graph, and, sampling indicates whether we try to use all vertices
163        (False) as starting points for finding separators or just a sampling (True).
164        The function returns a tuple containing a new graph which is the skeleton of
165        the input graph and a map from the graph nodes to the skeletal nodes. """
166    skel = Graph()
167    mapping = IntVector()
168    lib_py_gel.graph_LS_skeleton(g.obj, skel.obj, mapping.obj, sampling)
169    return skel, mapping

Skeletonize a graph using the local separators approach. The first argument, g, is the graph, and, sampling indicates whether we try to use all vertices (False) as starting points for finding separators or just a sampling (True). The function returns a tuple containing a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.

def MSLS_skeleton(g, grow_thresh=64):
171def MSLS_skeleton(g, grow_thresh=64):
172    """ Skeletonize a graph using the multi-scale local separators approach. The first argument,
173        g, is the graph, and, sampling indicates whether we try to use all vertices
174        (False) as starting points for finding separators or just a sampling (True).
175        The function returns a new graph which is the skeleton of the input graph. """
176    skel = Graph()
177    mapping = IntVector()
178    lib_py_gel.graph_MSLS_skeleton(g.obj, skel.obj, mapping.obj, grow_thresh)
179    return skel

Skeletonize a graph using the multi-scale local separators approach. The first argument, g, is the graph, and, sampling indicates whether we try to use all vertices (False) as starting points for finding separators or just a sampling (True). The function returns a new graph which is the skeleton of the input graph.

def MSLS_skeleton_and_map(g, grow_thresh=64):
181def MSLS_skeleton_and_map(g, grow_thresh=64):
182    """ Skeletonize a graph using the multi-scale local separators approach. The first argument,
183        g, is the graph, and, sampling indicates whether we try to use all vertices
184        (False) as starting points for finding separators or just a sampling (True).
185        The function returns a tuple containing a new graph which is the skeleton of
186        the input graph and a map from the graph nodes to the skeletal nodes. """
187    skel = Graph()
188    mapping = IntVector()
189    lib_py_gel.graph_MSLS_skeleton(g.obj, skel.obj, mapping.obj, grow_thresh)
190    return skel, mapping

Skeletonize a graph using the multi-scale local separators approach. The first argument, g, is the graph, and, sampling indicates whether we try to use all vertices (False) as starting points for finding separators or just a sampling (True). The function returns a tuple containing a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.

def front_skeleton_and_map(g, colors, intervals=100):
193def front_skeleton_and_map(g, colors, intervals=100):
194    """ Skeletonize a graph using the front separators approach. The first argument,
195        g, is the graph, and, colors is an nD array where each column contains a sequence
196        of floating point values - one for each node. We can have as many columns as needed
197        for the front separator computation. We can think of this as a coloring
198        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
199        of the nodes or some other function that indicates something about the structure of the
200        graph. The function returns a tuple containing a new graph which is the
201        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
202    skel = Graph()
203    mapping = IntVector()
204    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
205    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
206    print("N_col:", N_col)
207    pos = g.positions()
208    lib_py_gel.graph_front_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
209    return skel, mapping

Skeletonize a graph using the front separators approach. The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence of floating point values - one for each node. We can have as many columns as needed for the front separator computation. We can think of this as a coloring of the nodes, hence the name. In practice, a coloring might just be the x-coordinate of the nodes or some other function that indicates something about the structure of the graph. The function returns a tuple containing a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.

def front_skeleton(g, colors, intervals=100):
211def front_skeleton(g, colors, intervals=100):
212    """ Skeletonize a graph using the front separators approach. The first argument,
213        g, is the graph, and, colors is a nD array where each column contains a sequence
214        of floating point values - one for each node. We can have as many columns as needed
215        for the front separator computation. We can think of this as a coloring
216        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
217        of the nodes or some other function that indicates something about the structure of the
218        graph. The function returns a tuple containing a new graph which is the
219        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
220    skel = Graph()
221    mapping = IntVector()
222    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
223    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
224    print("N_col:", N_col)
225    pos = g.positions()
226    lib_py_gel.graph_front_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
227    return skel

Skeletonize a graph using the front separators approach. The first argument, g, is the graph, and, colors is a nD array where each column contains a sequence of floating point values - one for each node. We can have as many columns as needed for the front separator computation. We can think of this as a coloring of the nodes, hence the name. In practice, a coloring might just be the x-coordinate of the nodes or some other function that indicates something about the structure of the graph. The function returns a tuple containing a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.

def combined_skeleton_and_map(g, colors, intervals=100):
229def combined_skeleton_and_map(g, colors, intervals=100):
230    """ Skeletonize a graph using both the front separators approach and the multi scale local separators.
231        The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence
232        of floating point values - one for each node. We can have as many columns as needed
233        for the front separator computation. We can think of this as a coloring
234        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
235        of the nodes or some other function that indicates something about the structure of the
236        graph. The function returns a tuple containing a new graph which is the
237        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
238    skel = Graph()
239    mapping = IntVector()
240    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
241    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
242    print("N_col:", N_col)
243    pos = g.positions()
244    lib_py_gel.graph_combined_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
245    return skel, mapping

Skeletonize a graph using both the front separators approach and the multi scale local separators. The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence of floating point values - one for each node. We can have as many columns as needed for the front separator computation. We can think of this as a coloring of the nodes, hence the name. In practice, a coloring might just be the x-coordinate of the nodes or some other function that indicates something about the structure of the graph. The function returns a tuple containing a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.

def combined_skeleton(g, colors, intervals=100):
247def combined_skeleton(g, colors, intervals=100):
248    """ Skeletonize a graph using both the front separators approach and the multi scale local separators.
249        The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence
250        of floating point values - one for each node. We can have as many columns as needed
251        for the front separator computation. We can think of this as a coloring
252        of the nodes, hence the name. In practice, a coloring might just be the x-coordinate
253        of the nodes or some other function that indicates something about the structure of the
254        graph. The function returns a new graph which is the
255        skeleton of the input graph and a map from the graph nodes to the skeletal nodes. """
256    skel = Graph()
257    mapping = IntVector()
258    colors_flat = np.asarray(colors, dtype=ct.c_double, order='C')
259    N_col = 1 if len(colors_flat.shape)==1 else colors_flat.shape[1]
260    print("N_col:", N_col)
261    pos = g.positions()
262    lib_py_gel.graph_combined_skeleton(g.obj, skel.obj, mapping.obj, N_col, colors_flat.ctypes.data_as(ct.POINTER(ct.c_double)), intervals)
263    return skel

Skeletonize a graph using both the front separators approach and the multi scale local separators. The first argument, g, is the graph, and, colors is an nD array where each column contains a sequence of floating point values - one for each node. We can have as many columns as needed for the front separator computation. We can think of this as a coloring of the nodes, hence the name. In practice, a coloring might just be the x-coordinate of the nodes or some other function that indicates something about the structure of the graph. The function returns a new graph which is the skeleton of the input graph and a map from the graph nodes to the skeletal nodes.