Skip to content

Commit ac6e821

Browse files
committed
Add PlotLabel, PlotTheme->GraphLayout
1 parent aadd3e1 commit ac6e821

File tree

3 files changed

+383
-33
lines changed

3 files changed

+383
-33
lines changed

pymathics/graph/__main__.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
from collections import defaultdict
2727
from math import sqrt, ceil
2828

29+
DEFAULT_GRAPH_OPTIONS = {
30+
"VertexSize": "{}",
31+
"VertexStyle": "{}",
32+
"EdgeStyle": "{}",
33+
"EdgeWeight": "{}",
34+
"GraphLayout": "{}",
35+
"VertexLabeling": "False",
36+
"PlotLabel": "Null",
37+
}
38+
39+
DEFAULT_TREE_OPTIONS = {**DEFAULT_GRAPH_OPTIONS, **{"Directed" : "False"}}
40+
41+
2942
try:
3043
import networkx as nx
3144
except ImportError:
@@ -146,14 +159,7 @@ def _parse_property(expr, attr_dict=None):
146159
class _NetworkXBuiltin(Builtin):
147160
requires = ("networkx",)
148161

149-
options = {
150-
"VertexSize": "{}",
151-
"VertexStyle": "{}",
152-
"EdgeStyle": "{}",
153-
"EdgeWeight": "{}",
154-
"PlotTheme": "{}",
155-
"VertexLabeling": "False",
156-
}
162+
options = DEFAULT_GRAPH_OPTIONS
157163

158164
messages = {
159165
"graph": "Expected a graph at position 1 in ``.",
@@ -182,7 +188,6 @@ def _evaluate_atom(self, graph, options, compute):
182188
elif head == "System`List":
183189
return compute(_graph_from_list(graph.leaves, options))
184190

185-
186191
def __str__(self):
187192
return "-Graph-"
188193

@@ -336,6 +341,8 @@ def _normalize_edges(edges):
336341

337342
class Graph(Atom):
338343

344+
options = DEFAULT_GRAPH_OPTIONS
345+
339346
def __init__(self, G, **kwargs):
340347
super(Graph, self).__init__()
341348
self.options = kwargs.get("options", None)
@@ -452,7 +459,6 @@ def update_weights(self, evaluation):
452459
return weights
453460

454461

455-
456462
def _is_connected(G):
457463
if len(G) == 0: # empty graph?
458464
return True
@@ -519,7 +525,6 @@ def _create_graph(new_edges, new_edge_properties, options, from_graph=None):
519525
if "System`VertexStyle" in options:
520526
vertex_options = options["System`VertexStyle"].to_python()
521527

522-
523528
known_vertices = set(vertices)
524529
known_edges = set(edges)
525530

@@ -741,14 +746,7 @@ class GraphAtom(AtomBuiltin):
741746

742747
requires = ("networkx",)
743748

744-
options = {
745-
"VertexSize": "{}",
746-
"VertexStyle": "{}",
747-
"VertexLabeling": "False",
748-
"EdgeStyle": "{}",
749-
"DirectedEdges": "True",
750-
"PlotTheme": "Null",
751-
}
749+
options = DEFAULT_GRAPH_OPTIONS
752750

753751
def apply(self, graph, evaluation, options):
754752
"Graph[graph_List, OptionsPattern[%(name)s]]"

pymathics/graph/graph_generators.py

Lines changed: 167 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
from pymathics.graph.__main__ import _NetworkXBuiltin, nx, Graph
1+
from pymathics.graph.__main__ import (
2+
DEFAULT_TREE_OPTIONS,
3+
Graph,
4+
_NetworkXBuiltin,
5+
nx,
6+
)
27
from mathics.core.expression import String
38

9+
# TODO: this code can be DRY'd a bit.
10+
11+
412
class BalancedTree(_NetworkXBuiltin):
513
"""
614
<dl>
@@ -22,6 +30,8 @@ class BalancedTree(_NetworkXBuiltin):
2230
"mem": "Out of memory",
2331
}
2432

33+
options = DEFAULT_TREE_OPTIONS
34+
2535
def apply(self, r, h, expression, evaluation, options):
2636
"%(name)s[r_Integer, h_Integer, OptionsPattern[%(name)s]]"
2737
py_r = r.get_int_value()
@@ -35,19 +45,25 @@ def apply(self, r, h, expression, evaluation, options):
3545
evaluation.message(self.get_name(), "ilsmp2", expression)
3646
return
3747

48+
graph_create = nx.DiGraph if options["System`Directed"].to_python() else nx.Graph
49+
3850
try:
39-
G = nx.balanced_tree(py_r, py_h)
51+
G = nx.balanced_tree(py_r, py_h, create_using=graph_create)
4052
except MemoryError:
4153
evaluation.message(self.get_name(), "mem", expression)
4254
return
4355

44-
45-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("tree")
56+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
57+
"tree"
58+
)
4659
options["VertexLabeling"] = options["System`VertexLabeling"]
4760
g = Graph(G, options=options)
61+
4862
g.r = r
4963
g.h = h
5064
G.root = g.root = 0
65+
G.title = g.title = options["System`PlotLabel"]
66+
5167
return g
5268

5369

@@ -83,11 +99,14 @@ def apply(self, m1, m2, expression, evaluation, options):
8399

84100
G = nx.barbell_graph(py_m1, py_m2)
85101

86-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("spring")
102+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
103+
"spring"
104+
)
87105
options["VertexLabeling"] = options["System`VertexLabeling"]
88106
g = Graph(G, options=options)
89107
g.m1 = m1
90108
g.m2 = m2
109+
G.title = g.title = options["System`PlotLabel"]
91110
return g
92111

93112

@@ -113,6 +132,7 @@ class BinomialTree(_NetworkXBuiltin):
113132

114133
messages = {
115134
"ilsmp": "Expected a non-negative integer at position 1 in ``.",
135+
"mem": "Out of memory",
116136
}
117137

118138
def apply(self, n, expression, evaluation, options):
@@ -123,13 +143,20 @@ def apply(self, n, expression, evaluation, options):
123143
evaluation.message(self.get_name(), "ilsmp", expression)
124144
return
125145

126-
G = nx.binomial_tree(py_n)
146+
try:
147+
G = nx.binomial_tree(py_n)
148+
except MemoryError:
149+
evaluation.message(self.get_name(), "mem", expression)
150+
return
127151

128-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("tree")
152+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
153+
"tree"
154+
)
129155
options["VertexLabeling"] = options["System`VertexLabeling"]
130156
g = Graph(G, options=options)
131157
g.n = n
132158
G.root = g.root = 0
159+
G.title = g.title = options["System`PlotLabel"]
133160
return g
134161

135162

@@ -162,10 +189,13 @@ def apply(self, n, expression, evaluation, options):
162189

163190
G = nx.complete_graph(py_n)
164191

165-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("circular")
192+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
193+
"circular"
194+
)
166195
options["VertexLabeling"] = options["System`VertexLabeling"]
167196
g = Graph(G, options=options)
168-
g.n = n
197+
g.n = n
198+
G.title = g.title = options["System`PlotLabel"]
169199
return g
170200

171201
def apply_multipartite(self, n, evaluation, options):
@@ -194,8 +224,11 @@ class FullRAryTree(_NetworkXBuiltin):
194224
messages = {
195225
"ilsmp": "Expected a non-negative integer at position 1 in ``.",
196226
"ilsmp2": "Expected a non-negative integer at position 2 in ``.",
227+
"mem": "Out of memory",
197228
}
198229

230+
options = DEFAULT_TREE_OPTIONS
231+
199232
def apply(self, r, n, expression, evaluation, options):
200233
"%(name)s[r_Integer, n_Integer, OptionsPattern[%(name)s]]"
201234
py_r = r.get_int_value()
@@ -209,14 +242,23 @@ def apply(self, r, n, expression, evaluation, options):
209242
evaluation.message(self.get_name(), "ilsmp", expression)
210243
return
211244

212-
G = nx.full_rary_tree(py_r, py_n)
245+
graph_create = nx.DiGraph if options["System`Directed"].to_python() else nx.Graph
213246

214-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("tree")
247+
try:
248+
G = nx.full_rary_tree(py_r, py_n, create_using=graph_create)
249+
except MemoryError:
250+
evaluation.message(self.get_name(), "mem", expression)
251+
return
252+
253+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
254+
"tree"
255+
)
215256
options["VertexLabeling"] = options["System`VertexLabeling"]
216257
g = Graph(G, options=options)
217258
g.r = r
218259
g.n = n
219260
G.root = g.root = 0
261+
G.title = g.title = options["System`PlotLabel"]
220262
return g
221263

222264

@@ -247,8 +289,110 @@ def apply(self, n, expression, evaluation, options):
247289
G = nx.graph_atlas(py_n)
248290
g = Graph(G)
249291
g.n = n
292+
G.title = g.title = options["System`PlotLabel"]
250293
return g
251294

295+
296+
class HknHararyGraph(_NetworkXBuiltin):
297+
"""<dl>
298+
<dt>'HmnHararyGraph[$k$, $n$]'
299+
<dd>Returns the Harary graph with given node connectivity and node number.
300+
301+
This second generator gives the Harary graph that minimizes the
302+
number of edges in the graph with given node connectivity and
303+
number of nodes.
304+
305+
Harary, F. The Maximum Connectivity of a Graph. Proc. Nat. Acad. Sci. USA 48, 1142-1146, 1962.
306+
</dl>
307+
308+
>> HknHararyGraph[3, 10]
309+
= -Graph-
310+
311+
"""
312+
313+
messages = {
314+
"ilsmp": "Expected a non-negative integer at position 1 in ``.",
315+
"ilsmp2": "Expected a non-negative integer at position 2 in ``.",
316+
}
317+
318+
def apply(self, k, n, expression, evaluation, options):
319+
"%(name)s[k_Integer, n_Integer, OptionsPattern[%(name)s]]"
320+
py_k = k.get_int_value()
321+
322+
if py_k < 0:
323+
evaluation.message(self.get_name(), "ilsmp", expression)
324+
return
325+
326+
py_n = n.get_int_value()
327+
if py_n < 0:
328+
evaluation.message(self.get_name(), "ilsmp2", expression)
329+
return
330+
331+
from pymathics.graph.harary import hkn_harary_graph
332+
333+
G = hkn_harary_graph(py_k, py_n)
334+
335+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
336+
"spring"
337+
)
338+
options["VertexLabeling"] = options["System`VertexLabeling"]
339+
g = Graph(G, options=options)
340+
g.n = n
341+
G.root = g.root = 0
342+
G.title = g.title = options["System`PlotLabel"]
343+
return g
344+
345+
346+
class HmnHararyGraph(_NetworkXBuiltin):
347+
"""<dl>
348+
<dt>'HmnHararyGraph[$m$, $n$]'
349+
<dd>Returns the Harary graph with given numbers of nodes and edges.
350+
351+
This generator gives the Harary graph that maximizes the node
352+
connectivity with given number of nodes and given number of
353+
edges.
354+
355+
Harary, F. The Maximum Connectivity of a Graph. Proc. Nat. Acad. Sci. USA 48, 1142-1146, 1962.
356+
</dl>
357+
358+
>> HmnHararyGraph[5, 10]
359+
= -Graph-
360+
"""
361+
362+
messages = {
363+
"ilsmp": "Expected a non-negative integer at position 1 in ``.",
364+
"ilsmp2": "Expected a non-negative integer at position 2 in ``.",
365+
}
366+
367+
def apply(self, n, m, expression, evaluation, options):
368+
"%(name)s[n_Integer, m_Integer, OptionsPattern[%(name)s]]"
369+
py_n = n.get_int_value()
370+
371+
if py_n < 0:
372+
evaluation.message(self.get_name(), "ilsmp", expression)
373+
return
374+
375+
py_m = m.get_int_value()
376+
377+
if py_m < 0:
378+
evaluation.message(self.get_name(), "ilsmp2", expression)
379+
return
380+
381+
from pymathics.graph.harary import hnm_harary_graph
382+
383+
G = hnm_harary_graph(py_n, py_m)
384+
385+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
386+
"circular"
387+
)
388+
options["VertexLabeling"] = options["System`VertexLabeling"]
389+
g = Graph(G, options=options)
390+
g.n = n
391+
G.root = g.root = 0
392+
G.title = g.title = options["System`PlotLabel"]
393+
return g
394+
395+
252396
class RandomTree(_NetworkXBuiltin):
253397
"""
254398
<dl>
@@ -275,7 +419,9 @@ def apply(self, n, expression, evaluation, options):
275419

276420
G = nx.random_tree(py_n)
277421

278-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("tree")
422+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
423+
"circular"
424+
)
279425
options["VertexLabeling"] = options["System`VertexLabeling"]
280426
g = Graph(G, options=options)
281427
g.n = n
@@ -306,11 +452,18 @@ def apply(self, n, expression, evaluation, options):
306452
evaluation.message(self.get_name(), "ilsmp", expression)
307453
return
308454

309-
G = nx.star_graph(py_n)
455+
try:
456+
G = nx.star_graph(py_n)
457+
except MemoryError:
458+
evaluation.message(self.get_name(), "mem", expression)
459+
return
310460

311-
options["PlotTheme"] = options["System`PlotTheme"].get_string_value() or String("spring")
461+
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
462+
"spring"
463+
)
312464
options["VertexLabeling"] = options["System`VertexLabeling"]
313465

314466
g = Graph(G, options=options)
315467
g.n = n
468+
G.title = g.title = options["System`PlotLabel"]
316469
return g

0 commit comments

Comments
 (0)