Skip to content

Commit b32477a

Browse files
committed
DRY graph generators
1 parent ac6e821 commit b32477a

File tree

2 files changed

+95
-132
lines changed

2 files changed

+95
-132
lines changed

pymathics/graph/__main__.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,11 @@
1515
Atom,
1616
Integer,
1717
Real,
18-
system_symbols_dict,
1918
from_python,
2019
)
21-
from mathics.core.util import robust_min
2220
from mathics.builtin.patterns import Matcher
2321

2422
from inspect import isgenerator
25-
from itertools import permutations
26-
from collections import defaultdict
27-
from math import sqrt, ceil
2823

2924
DEFAULT_GRAPH_OPTIONS = {
3025
"VertexSize": "{}",
@@ -58,15 +53,7 @@ def _shell_layout(G):
5853

5954

6055
def _generic_layout(G, warn):
61-
try:
62-
import pydot
63-
except ImportError:
64-
pass
65-
else:
66-
return nx.nx_pydot.graphviz_layout(G, prog="dot")
67-
68-
warn("Could not find pydot; graph layout quality might be low.")
69-
return nx.drawing.fruchterman_reingold_layout(G, pos=None, k=1.0)
56+
return nx.nx_pydot.graphviz_layout(G, prog="dot")
7057

7158

7259
def _path_layout(G, root):
@@ -345,7 +332,6 @@ class Graph(Atom):
345332

346333
def __init__(self, G, **kwargs):
347334
super(Graph, self).__init__()
348-
self.options = kwargs.get("options", None)
349335
self.G = G
350336

351337
@property

pymathics/graph/graph_generators.py

Lines changed: 94 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,41 @@
44
_NetworkXBuiltin,
55
nx,
66
)
7-
from mathics.core.expression import String
7+
from mathics.core.expression import Integer, String
8+
from typing import Callable, Optional
9+
10+
# TODO: Can this code can be DRY'd more?
11+
12+
13+
def graph_helper(
14+
graph_generator_func: Callable,
15+
options: dict,
16+
can_digraph: bool,
17+
graph_layout: str,
18+
root: Optional[int] = None,
19+
*args,
20+
**kwargs
21+
) -> Optional[Callable]:
22+
should_digraph = can_digraph and options["System`Directed"].to_python()
23+
try:
24+
G = (
25+
graph_generator_func(*args, create_using=nx.DiGraph, **kwargs)
26+
if should_digraph
27+
else graph_generator_func(*args, **kwargs)
28+
)
29+
except MemoryError:
30+
evaluation.message(self.get_name(), "mem", expression)
31+
return None
32+
G.graph_layout = options["System`GraphLayout"].get_string_value() or String(
33+
graph_layout
34+
)
35+
G.vertex_labeling = options["System`VertexLabeling"]
36+
g = Graph(G)
837

9-
# TODO: this code can be DRY'd a bit.
38+
if root is not None:
39+
G.root = g.root = root
40+
G.title = g.title = options["System`PlotLabel"]
41+
return g
1042

1143

1244
class BalancedTree(_NetworkXBuiltin):
@@ -38,32 +70,19 @@ def apply(self, r, h, expression, evaluation, options):
3870

3971
if py_r < 0:
4072
evaluation.message(self.get_name(), "ilsmp", expression)
41-
return
73+
return None
4274

4375
py_h = h.get_int_value()
4476
if py_h < 0:
4577
evaluation.message(self.get_name(), "ilsmp2", expression)
46-
return
47-
48-
graph_create = nx.DiGraph if options["System`Directed"].to_python() else nx.Graph
49-
50-
try:
51-
G = nx.balanced_tree(py_r, py_h, create_using=graph_create)
52-
except MemoryError:
53-
evaluation.message(self.get_name(), "mem", expression)
54-
return
55-
56-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
57-
"tree"
58-
)
59-
options["VertexLabeling"] = options["System`VertexLabeling"]
60-
g = Graph(G, options=options)
61-
62-
g.r = r
63-
g.h = h
64-
G.root = g.root = 0
65-
G.title = g.title = options["System`PlotLabel"]
66-
78+
return None
79+
80+
args = (py_r, py_h)
81+
g = graph_helper(nx.balanced_tree, options, True, "tree", 0, *args)
82+
if not g:
83+
return None
84+
g.G.r = r
85+
g.G.h = h
6786
return g
6887

6988

@@ -74,7 +93,7 @@ class BarbellGraph(_NetworkXBuiltin):
7493
<dd>Barbell Graph: two complete graphs connected by a path.
7594
</dl>
7695
77-
>> BarBellGraph[2, 3]
96+
>> BarBellGraph[4, 1]
7897
= -Graph-
7998
8099
"""
@@ -97,16 +116,13 @@ def apply(self, m1, m2, expression, evaluation, options):
97116
evaluation.message(self.get_name(), "ilsmp", expression)
98117
return
99118

100-
G = nx.barbell_graph(py_m1, py_m2)
119+
args = (py_m1, py_m2)
120+
g = graph_helper(nx.barbell_graph, options, False, "spring", None, *args)
121+
if not g:
122+
return None
101123

102-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
103-
"spring"
104-
)
105-
options["VertexLabeling"] = options["System`VertexLabeling"]
106-
g = Graph(G, options=options)
107-
g.m1 = m1
108-
g.m2 = m2
109-
G.title = g.title = options["System`PlotLabel"]
124+
g.G.m1 = m1
125+
g.G.m2 = m2
110126
return g
111127

112128

@@ -143,20 +159,11 @@ def apply(self, n, expression, evaluation, options):
143159
evaluation.message(self.get_name(), "ilsmp", expression)
144160
return
145161

146-
try:
147-
G = nx.binomial_tree(py_n)
148-
except MemoryError:
149-
evaluation.message(self.get_name(), "mem", expression)
150-
return
151-
152-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
153-
"tree"
154-
)
155-
options["VertexLabeling"] = options["System`VertexLabeling"]
156-
g = Graph(G, options=options)
157-
g.n = n
158-
G.root = g.root = 0
159-
G.title = g.title = options["System`PlotLabel"]
162+
args = (py_n,)
163+
g = graph_helper(nx.binomial_tree, options, False, "tree", 0, *args)
164+
if not g:
165+
return None
166+
g.G.n = n
160167
return g
161168

162169

@@ -189,9 +196,9 @@ def apply(self, n, expression, evaluation, options):
189196

190197
G = nx.complete_graph(py_n)
191198

192-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
193-
"circular"
194-
)
199+
options["GraphLayout"] = options[
200+
"System`GraphLayout"
201+
].get_string_value() or String("circular")
195202
options["VertexLabeling"] = options["System`VertexLabeling"]
196203
g = Graph(G, options=options)
197204
g.n = n
@@ -242,36 +249,26 @@ def apply(self, r, n, expression, evaluation, options):
242249
evaluation.message(self.get_name(), "ilsmp", expression)
243250
return
244251

245-
graph_create = nx.DiGraph if options["System`Directed"].to_python() else nx.Graph
246-
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+
args = (py_r, py_n)
253+
g = graph_helper(nx.full_rary_tree, options, True, "tree", 0, *args)
254+
if not g:
255+
return None
252256

253-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
254-
"tree"
255-
)
256-
options["VertexLabeling"] = options["System`VertexLabeling"]
257-
g = Graph(G, options=options)
258-
g.r = r
259-
g.n = n
260-
G.root = g.root = 0
261-
G.title = g.title = options["System`PlotLabel"]
257+
g.G.r = r
258+
g.G.n = n
262259
return g
263260

264261

265262
class GraphAtlas(_NetworkXBuiltin):
266-
"""
267-
<dl>
263+
"""<dl>
268264
<dt>'GraphAtlas[$n$]'
269-
<dd>gives graph number $i$ from the Networkx's Graph Atlas. There are about 1200 of them.
265+
<dd>gives graph number $i$ from the Networkx's Graph
266+
Atlas. There are about 1200 of them and get large as $i$
267+
increases.
270268
</dl>
271269
272270
>> GraphAtlas[1000]
273271
= -Graph-
274-
275272
"""
276273

277274
messages = {
@@ -286,10 +283,11 @@ def apply(self, n, expression, evaluation, options):
286283
evaluation.message(self.get_name(), "ilsmp", expression)
287284
return
288285

289-
G = nx.graph_atlas(py_n)
290-
g = Graph(G)
286+
args = (py_n,)
287+
g = graph_helper(nx.graph_atlas, options, False, "spring", None, *args)
288+
if not g:
289+
return None
291290
g.n = n
292-
G.title = g.title = options["System`PlotLabel"]
293291
return g
294292

295293

@@ -330,16 +328,12 @@ def apply(self, k, n, expression, evaluation, options):
330328

331329
from pymathics.graph.harary import hkn_harary_graph
332330

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"]
331+
args = (py_k, py_n)
332+
g = graph_helper(hkn_harary_graph, options, False, "circular", None, *args)
333+
if not g:
334+
return None
335+
g.k = py_k
336+
g.n = py_n
343337
return g
344338

345339

@@ -380,16 +374,12 @@ def apply(self, n, m, expression, evaluation, options):
380374

381375
from pymathics.graph.harary import hnm_harary_graph
382376

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"]
377+
args = (py_n, py_m)
378+
g = graph_helper(hmn_harary_graph, options, False, "circular", None, *args)
379+
if not g:
380+
return None
381+
g.n = py_n
382+
g.m = py_m
393383
return g
394384

395385

@@ -417,15 +407,11 @@ def apply(self, n, expression, evaluation, options):
417407
evaluation.message(self.get_name(), "ilsmp", expression)
418408
return
419409

420-
G = nx.random_tree(py_n)
421-
422-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
423-
"circular"
424-
)
425-
options["VertexLabeling"] = options["System`VertexLabeling"]
426-
g = Graph(G, options=options)
427-
g.n = n
428-
G.root = g.root = 0
410+
args = (py_n,)
411+
g = graph_helper(nx.random_tree, options, False, "tree", 0, *args)
412+
if not g:
413+
return None
414+
g.G.n = n
429415
return g
430416

431417

@@ -452,18 +438,9 @@ def apply(self, n, expression, evaluation, options):
452438
evaluation.message(self.get_name(), "ilsmp", expression)
453439
return
454440

455-
try:
456-
G = nx.star_graph(py_n)
457-
except MemoryError:
458-
evaluation.message(self.get_name(), "mem", expression)
459-
return
460-
461-
options["GraphLayout"] = options["System`GraphLayout"].get_string_value() or String(
462-
"spring"
463-
)
464-
options["VertexLabeling"] = options["System`VertexLabeling"]
465-
466-
g = Graph(G, options=options)
467-
g.n = n
468-
G.title = g.title = options["System`PlotLabel"]
441+
args = (py_n,)
442+
g = graph_helper(nx.star_graph, options, False, "spring", 0, *args)
443+
if not g:
444+
return None
445+
g.G.n = n
469446
return g

0 commit comments

Comments
 (0)