Skip to content

Commit e50c270

Browse files
authored
Merge pull request #66 from MunchLab/mapper_graph
Added a basic mapper graph class skeleton
2 parents d372b56 + def0128 commit e50c270

File tree

149 files changed

+4606
-1351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+4606
-1351
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,4 @@ The code is a compilation of work done by [Elizabeth Munch](http://www.elizabeth
4040

4141
## Contact Information
4242

43-
- [Liz Munch](http://www.elizabethmunch.com): [muncheli@msu.edu](mailto:muncheli@msu.edu)
44-
- [Danielle Barnes](https://github.com/barnesd8): [barnesd8@msu.edu](mailto:barnesd8@msu.edu)
43+
- [Liz Munch](http://www.elizabethmunch.com): [muncheli@msu.edu](mailto:muncheli@msu.edu)

cereeberus/cereeberus/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
from .reeb.reebgraph import ReebGraph
44
from .reeb.merge import MergeTree
5+
from .reeb.mapper import MapperGraph
56
from .reeb.embeddedgraph import EmbeddedGraph
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from cereeberus import ReebGraph, MapperGraph
2+
from cereeberus.data import ex_graphs
3+
4+
def torus(delta = .1, seed=None):
5+
'''
6+
Returns the Mapper graph of a simple upright torus as a MapperGraph class.
7+
8+
Parameters:
9+
delta (float): Optional. The delta value to use for the Mapper graph.
10+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
11+
12+
Returns:
13+
Mapper: The mapper graph of the torus.
14+
15+
16+
.. figure:: ../../images/torus_mapper.png
17+
:figwidth: 400px
18+
19+
'''
20+
return ReebGraph(ex_graphs.torus_graph(), seed=seed).to_mapper(delta = delta)
21+
22+
def dancing_man(delta = .1, seed=None):
23+
'''
24+
Returns the Mapper graph of the dancing man as a MapperGraph class.
25+
26+
Parameters:
27+
28+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
29+
30+
Returns:
31+
MapperGraph: The mapper graph of the dancing man.
32+
33+
.. figure:: ../../images/dancing_man_mapper.png
34+
:figwidth: 400px
35+
36+
'''
37+
return ReebGraph(ex_graphs.dancing_man(), seed=seed).to_mapper(delta = delta)
38+
39+
def juggling_man(delta = .1, seed=None):
40+
'''
41+
Returns a modified mapper graph of the juggling man as a MapperGraph class. Some vertex locations were moved to make them integers.
42+
43+
Parameters:
44+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
45+
46+
Returns:
47+
MapperGraph: The mapper graph of the juggling man.
48+
49+
.. figure:: ../../images/juggling_man_mapper.png
50+
:figwidth: 400px
51+
52+
'''
53+
R = ReebGraph(ex_graphs.juggling_man(), seed=seed)
54+
R.f[9] = 7
55+
R.f[8] = 6
56+
R.f[10] = 5
57+
58+
return R.to_mapper(delta = delta)
59+
60+
def simple_loops(delta = .1, seed=None):
61+
'''
62+
Returns the mapper graph of the simple loops example.
63+
64+
Parameters:
65+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
66+
67+
Returns:
68+
MapperGraph: The mapper graph of the simple loops example.
69+
70+
.. figure:: ../../images/simple_loops_mapper.png
71+
:figwidth: 400px
72+
73+
'''
74+
return ReebGraph(ex_graphs.simple_loops(), seed=seed).to_mapper()
75+
76+
def simple_loops_unordered(seed=None):
77+
'''
78+
Returns the mapper graph of the unordered simple loops example.
79+
80+
Parameters:
81+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
82+
83+
Returns:
84+
MapperGraph: The mapper graph of the simple loops example.
85+
86+
'''
87+
return ReebGraph(ex_graphs.simple_loops_unordered(), seed=seed).to_mapper()

cereeberus/cereeberus/data/ex_reebgraphs.py

Lines changed: 45 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,91 +2,74 @@
22
from cereeberus.data import ex_graphs
33

44
def torus(seed=None):
5-
def torus(seed=None):
6-
'''
7-
Returns the Reeb graph of a simple upright torus as a ReebGraph class.
8-
9-
Parameters:
10-
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
11-
12-
Returns:
13-
ReebGraph: The Reeb graph of the torus.
14-
15-
.. figure:: images/torus.png
16-
:figwidth: 200px
17-
18-
'''
19-
return ReebGraph(ex_graphs.torus_graph(), seed=seed)
20-
21-
def dancing_man(seed=None):
22-
'''
23-
Returns the Reeb graph of the dancing man as a ReebGraph class.
24-
25-
Parameters:
26-
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
27-
28-
Returns:
29-
ReebGraph: The Reeb graph of the dancing man.
30-
'''
31-
return ReebGraph(ex_graphs.dancing_man(), seed=seed)
32-
33-
def juggling_man(seed=None):
34-
'''
35-
Returns the Reeb graph of the juggling man as a ReebGraph class.
36-
37-
Parameters:
38-
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
39-
40-
Returns:
41-
ReebGraph: The Reeb graph of the juggling man.
42-
'''
43-
return ReebGraph(ex_graphs.juggling_man(), seed=seed)
44-
45-
def simple_loops(seed=None):
46-
'''
47-
Returns the Reeb graph of the simple loops example.
48-
49-
Parameters:
50-
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
51-
52-
Returns:
53-
ReebGraph: The Reeb graph of the simple loops example.
54-
'''
55-
return ReebGraph(ex_graphs.simple_loops(), seed=seed)
5+
'''
6+
Returns the Reeb graph of a simple upright torus as a ReebGraph class.
567
57-
def simple_loops_unordered(seed=None):
58-
'''
59-
Returns the Reeb graph of the simple loops example.
8+
Parameters:
9+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
6010
61-
Parameters:
62-
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
11+
Returns:
12+
ReebGraph: The Reeb graph of the torus.
13+
14+
.. figure:: ../../images/torus.png
15+
:figwidth: 400px
6316
64-
Returns:
65-
ReebGraph: The Reeb graph of the simple loops example.
66-
'''
67-
return ReebGraph(ex_graphs.simple_loops_unordered(), seed=seed)
17+
'''
6818
return ReebGraph(ex_graphs.torus_graph(), seed=seed)
6919

7020
def dancing_man(seed=None):
7121
'''
7222
Returns the Reeb graph of the dancing man as a ReebGraph class.
23+
24+
Parameters:
25+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
26+
27+
Returns:
28+
ReebGraph: The Reeb graph of the dancing man.
29+
30+
.. figure:: ../../images/dancing_man.png
31+
:figwidth: 400px
7332
'''
7433
return ReebGraph(ex_graphs.dancing_man(), seed=seed)
7534

7635
def juggling_man(seed=None):
7736
'''
7837
Returns the Reeb graph of the juggling man as a ReebGraph class.
38+
39+
Parameters:
40+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
41+
42+
Returns:
43+
ReebGraph: The Reeb graph of the juggling man.
44+
45+
.. figure:: ../../images/juggling_man.png
46+
:figwidth: 400px
7947
'''
8048
return ReebGraph(ex_graphs.juggling_man(), seed=seed)
8149

8250
def simple_loops(seed=None):
8351
'''
84-
Returns the Reeb graph of the simple loops example
52+
Returns the Reeb graph of the simple loops example.
53+
54+
Parameters:
55+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
56+
57+
Returns:
58+
ReebGraph: The Reeb graph of the simple loops example.
59+
60+
.. figure:: ../../images/simple_loops.png
61+
:figwidth: 400px
8562
'''
8663
return ReebGraph(ex_graphs.simple_loops(), seed=seed)
8764

8865
def simple_loops_unordered(seed=None):
8966
'''
90-
Returns the Reeb graph of the simple loops example
67+
Returns the Reeb graph of the simple loops example.
68+
69+
Parameters:
70+
seed (int): Optional. The seed to use for the random number generator, which only controls the layout function.
71+
72+
Returns:
73+
ReebGraph: The Reeb graph of the simple loops example.
9174
'''
9275
return ReebGraph(ex_graphs.simple_loops_unordered(), seed=seed)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__all__ = ['reebgraph', 'merge']
1+
__all__ = ['reebgraph', 'merge', 'mapper', 'embeddedgraph']
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from cereeberus import ReebGraph
2+
3+
class MapperGraph(ReebGraph):
4+
"""
5+
A mapper graph structure. This inherits the properties of the Reeb graph in that it is a graph with a function given on the vertices, but with some additional requirements.
6+
7+
- The values are integers in some range, [n_low, n_low+1, \cdots, n_high], although we consider the funciton values to be [\delta * n_low, \delta* (n_low+1), \cdots, \delta * n_high] for a stored delta
8+
- If an edge crosses a value, it has a vertex (so that the inverse image of any integer is only vertices, not interiors of edges)
9+
- An internal delta is stored so that this can be interpreted as function values [\delta * n_low, \delta* (n_low+1), \cdots, \delta * n_high]
10+
"""
11+
12+
def __init__(self,
13+
G=None, f={}, delta = None, seed = None, verbose=False):
14+
15+
# Check that $f$ values are only integers
16+
if not all([isinstance(f[v], int) for v in f]):
17+
raise ValueError("Function values must be integers.")
18+
if delta is None:
19+
self.delta = 1
20+
else:
21+
self.delta = delta
22+
23+
super().__init__(G, f, seed, verbose)
24+
25+
self.mapperify()
26+
27+
def add_edge(self, u, v, reset_pos=True):
28+
"""
29+
Add an edge to the graph. This will also update the internal structure to make sure it satisfies the mapper properties.
30+
"""
31+
super().add_edge(u, v, reset_pos)
32+
self.mapperify()
33+
34+
35+
36+
def mapperify(self):
37+
"""
38+
Take the internal structure and make sure it satisfies the requirement that all edges have adjacent function values.
39+
"""
40+
41+
# If we're initializing with nothing, this should pass.
42+
# Note that if self.n_low is None, then self.n_high and self.delta
43+
# are both None as well but I am not currently checking that.
44+
try:
45+
n_low = min(self.f.values())
46+
n_high = max(self.f.values())
47+
48+
except:
49+
return
50+
51+
last_vert_name = max(self.nodes())
52+
53+
for i in range(n_low,n_high+1):
54+
e_list = [e for e in self.edges() if self.f[e[0]] < i and self.f[e[1]] > i]
55+
56+
for e in e_list:
57+
w_name = self.next_vert_name(last_vert_name)
58+
self.subdivide_edge(*e,w_name, i)
59+
60+
last_vert_name = w_name
61+
62+
63+
def add_node(self, vertex, f_vertex, reset_pos=True):
64+
65+
"""
66+
Same as adding a node in Reeb, but with the additional requirement that the function value is an integer.
67+
"""
68+
69+
if not isinstance(f_vertex, int):
70+
raise ValueError("Function values must be integers.")
71+
return super().add_node(vertex, f_vertex, reset_pos)
72+
73+
74+
75+
def set_pos_from_f(self, seed=None, verbose=False):
76+
77+
"""
78+
Same as the Reeb graph function, but we want to draw the vertex locations at delta*function value.
79+
"""
80+
super().set_pos_from_f(seed, verbose)
81+
82+
for v in self.nodes():
83+
self.pos_f[v] = (self.pos_f[v][0],self.delta * self.f[v])
84+
85+
def induced_subgraph(self, nodes):
86+
"""
87+
Returns the subgraph of the mapper graph induced by the nodes in the list nodes.
88+
89+
Parameters:
90+
nodes (list): The list of nodes to include in the subgraph.
91+
92+
Returns:
93+
MapperGraph
94+
"""
95+
R = super().induced_subgraph(nodes)
96+
return R.to_mapper(self.delta)
97+

0 commit comments

Comments
 (0)