Skip to content

Commit 588e037

Browse files
committed
track module change in mathics-core...
Also, use annotations more often. Remove links to older NetworkX 2.8 docs
1 parent 57522db commit 588e037

File tree

1 file changed

+149
-136
lines changed

1 file changed

+149
-136
lines changed

pymathics/graph/base.py

Lines changed: 149 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
Core routines for working with Graphs.
55
"""
66

7-
# uses networkx
7+
# uses NetworkX
88

99
from collections import defaultdict
1010
from inspect import isgenerator
1111
from typing import Callable, Optional, Union
1212

13-
from mathics.builtin.no_meaning import (
13+
from mathics.builtin.no_meaning.infix_extra import (
1414
DirectedEdge as GenericDirectedEdge,
1515
UndirectedEdge as GenericUndirectedEdge,
1616
)
@@ -19,6 +19,7 @@
1919
from mathics.core.atoms import Atom, Integer, Integer0, Integer1, Integer2, String
2020
from mathics.core.convert.expression import ListExpression, from_python, to_mathics_list
2121
from mathics.core.element import BaseElement
22+
from mathics.core.evaluation import Evaluation
2223
from mathics.core.expression import Expression
2324
from mathics.core.pattern import pattern_objects
2425
from mathics.core.symbols import Symbol, SymbolList, SymbolTrue
@@ -133,133 +134,6 @@ def has_directed_option(options: dict) -> bool:
133134
return options.get("System`DirectedEdges", False)
134135

135136

136-
def _process_graph_options(g, options: dict) -> None:
137-
"""
138-
Handle common graph-related options like VertexLabels, PlotLabel, VertexShape, etc.
139-
"""
140-
# FIXME: for now we are adding both to both g and g.G.
141-
# g is where it is used in format. However we should wrap this as our object.
142-
# Access in G which might be better, currently isn't used.
143-
g.G.vertex_labels = g.vertex_labels = (
144-
options["System`VertexLabels"].to_python()
145-
if "System`VertexLabels" in options
146-
else False
147-
)
148-
shape = (
149-
options["System`VertexShape"].get_string_value()
150-
if "System`VertexShape" in options
151-
else "Circle"
152-
)
153-
154-
g.G.node_shape = g.node_shape = WL_MARKER_TO_NETWORKX.get(shape, shape)
155-
156-
color = (
157-
options["System`VertexStyle"].get_string_value()
158-
if "System`VertexStyle" in options
159-
else "Blue"
160-
)
161-
162-
g.graph_layout = (
163-
options["System`GraphLayout"].get_string_value()
164-
if "System`GraphLayout" in options
165-
else ""
166-
)
167-
168-
g.G.graph_layout = g.graph_layout = WL_LAYOUT_TO_NETWORKX.get(
169-
g.graph_layout, g.graph_layout
170-
)
171-
172-
g.G.node_color = g.node_color = WL_COLOR_TO_NETWORKX.get(color, color)
173-
174-
g.G.title = g.title = (
175-
options["System`PlotLabel"].get_string_value()
176-
if "System`PlotLabel" in options
177-
else None
178-
)
179-
180-
181-
def _circular_layout(G):
182-
return nx.drawing.circular_layout(G, scale=1)
183-
184-
185-
def _spectral_layout(G):
186-
return nx.drawing.spectral_layout(G, scale=2)
187-
188-
189-
def _shell_layout(G):
190-
return nx.drawing.shell_layout(G, scale=2)
191-
192-
193-
def _generic_layout(G, warn):
194-
return nx.nx_pydot.graphviz_layout(G, prog="dot")
195-
196-
197-
def _path_layout(G, root):
198-
v = root
199-
x = 0
200-
y = 0
201-
202-
k = 0
203-
d = 0
204-
205-
pos = {}
206-
neighbors = G.neighbors(v)
207-
208-
for _ in range(len(G)):
209-
pos[v] = (x, y)
210-
211-
if not neighbors:
212-
break
213-
try:
214-
v = next(neighbors) if isgenerator(neighbors) else neighbors[0]
215-
except StopIteration:
216-
break
217-
neighbors = G.neighbors(v)
218-
219-
if k == 0:
220-
if d < 1 or neighbors:
221-
d += 1
222-
x += d
223-
elif k == 1:
224-
y += d
225-
elif k == 2:
226-
if neighbors:
227-
d += 1
228-
x -= d
229-
elif k == 3:
230-
y -= d
231-
232-
k = (k + 1) % 4
233-
234-
return pos
235-
236-
237-
def _auto_layout(G, warn):
238-
path_root = None
239-
240-
for v, d in G.degree(G.nodes):
241-
if d == 1 and G.neighbors(v):
242-
path_root = v
243-
elif d > 2:
244-
path_root = None
245-
break
246-
247-
if path_root is not None:
248-
return _path_layout(G, path_root)
249-
else:
250-
return _generic_layout(G, warn)
251-
252-
253-
def _convert_networkx_graph(G, options):
254-
mapping = dict((v, Integer(i)) for i, v in enumerate(G.nodes))
255-
G = nx.relabel_nodes(G, mapping)
256-
[Expression(SymbolUndirectedEdge, u, v) for u, v in G.edges]
257-
return Graph(
258-
G,
259-
**options,
260-
)
261-
262-
263137
_default_minimum_distance = 0.3
264138

265139

@@ -424,7 +298,7 @@ def __hash__(self):
424298
def __str__(self):
425299
return "-Graph-"
426300

427-
def atom_to_boxes(self, f, evaluation) -> "GraphBox":
301+
def atom_to_boxes(self, f, evaluation: Evaluation) -> "GraphBox":
428302
return GraphBox(self.G)
429303

430304
def add_edges(self, new_edges, new_edge_properties):
@@ -439,7 +313,7 @@ def add_vertices(self, *vertices_to_add):
439313
G.add_nodes_from(vertices_to_add)
440314
return Graph(G)
441315

442-
def coalesced_graph(self, evaluation):
316+
def coalesced_graph(self, evaluation: Evaluation):
443317
if not isinstance(self.G, (nx.MultiDiGraph, nx.MultiGraph)):
444318
return self.G, "WEIGHT"
445319

@@ -603,6 +477,133 @@ def _graph_from_list(rules, options, new_vertices=None):
603477
)
604478

605479

480+
def _process_graph_options(g: Graph, options: dict) -> None:
481+
"""
482+
Handle common graph-related options like VertexLabels, PlotLabel, VertexShape, etc.
483+
"""
484+
# FIXME: for now we are adding both to both g and g.G.
485+
# g is where it is used in format. However we should wrap this as our object.
486+
# Access in G which might be better, currently isn't used.
487+
g.G.vertex_labels = g.vertex_labels = (
488+
options["System`VertexLabels"].to_python()
489+
if "System`VertexLabels" in options
490+
else False
491+
)
492+
shape = (
493+
options["System`VertexShape"].get_string_value()
494+
if "System`VertexShape" in options
495+
else "Circle"
496+
)
497+
498+
g.G.node_shape = g.node_shape = WL_MARKER_TO_NETWORKX.get(shape, shape)
499+
500+
color = (
501+
options["System`VertexStyle"].get_string_value()
502+
if "System`VertexStyle" in options
503+
else "Blue"
504+
)
505+
506+
g.graph_layout = (
507+
options["System`GraphLayout"].get_string_value()
508+
if "System`GraphLayout" in options
509+
else ""
510+
)
511+
512+
g.G.graph_layout = g.graph_layout = WL_LAYOUT_TO_NETWORKX.get(
513+
g.graph_layout, g.graph_layout
514+
)
515+
516+
g.G.node_color = g.node_color = WL_COLOR_TO_NETWORKX.get(color, color)
517+
518+
g.G.title = g.title = (
519+
options["System`PlotLabel"].get_string_value()
520+
if "System`PlotLabel" in options
521+
else None
522+
)
523+
524+
525+
def _circular_layout(G: Graph):
526+
return nx.drawing.circular_layout(G, scale=1)
527+
528+
529+
def _spectral_layout(G: Graph):
530+
return nx.drawing.spectral_layout(G, scale=2)
531+
532+
533+
def _shell_layout(G: Graph):
534+
return nx.drawing.shell_layout(G, scale=2)
535+
536+
537+
def _generic_layout(G, warn):
538+
return nx.nx_pydot.graphviz_layout(G, prog="dot")
539+
540+
541+
def _path_layout(G, root):
542+
v = root
543+
x = 0
544+
y = 0
545+
546+
k = 0
547+
d = 0
548+
549+
pos = {}
550+
neighbors = G.neighbors(v)
551+
552+
for _ in range(len(G)):
553+
pos[v] = (x, y)
554+
555+
if not neighbors:
556+
break
557+
try:
558+
v = next(neighbors) if isgenerator(neighbors) else neighbors[0]
559+
except StopIteration:
560+
break
561+
neighbors = G.neighbors(v)
562+
563+
if k == 0:
564+
if d < 1 or neighbors:
565+
d += 1
566+
x += d
567+
elif k == 1:
568+
y += d
569+
elif k == 2:
570+
if neighbors:
571+
d += 1
572+
x -= d
573+
elif k == 3:
574+
y -= d
575+
576+
k = (k + 1) % 4
577+
578+
return pos
579+
580+
581+
def _auto_layout(G: Graph, warn):
582+
path_root = None
583+
584+
for v, d in G.degree(G.nodes):
585+
if d == 1 and G.neighbors(v):
586+
path_root = v
587+
elif d > 2:
588+
path_root = None
589+
break
590+
591+
if path_root is not None:
592+
return _path_layout(G, path_root)
593+
else:
594+
return _generic_layout(G, warn)
595+
596+
597+
def _convert_networkx_graph(G: Graph, options: dict):
598+
mapping = dict((v, Integer(i)) for i, v in enumerate(G.nodes))
599+
G = nx.relabel_nodes(G, mapping)
600+
[Expression(SymbolUndirectedEdge, u, v) for u, v in G.edges]
601+
return Graph(
602+
G,
603+
**options,
604+
)
605+
606+
606607
def _create_graph(
607608
new_edges, new_edge_properties, options, from_graph=None, new_vertices=None
608609
):
@@ -813,7 +814,9 @@ def full_new_edge_properties(new_edge_style):
813814

814815

815816
class _PatternList(_NetworkXBuiltin):
816-
def eval(self, graph, expression, evaluation, options):
817+
def eval(
818+
self, graph, expression: Expression, evaluation: Evaluation, options: dict
819+
):
817820
"%(name)s[graph_, OptionsPattern[%(name)s]]"
818821
graph = self._build_graph(graph, evaluation, options, expression)
819822
if graph:
@@ -903,8 +906,6 @@ class DirectedEdge(GenericDirectedEdge):
903906
Edge of a <url>
904907
:Directed graph:
905908
https://en.wikipedia.org/wiki/Directed_graph</url> (<url>
906-
:NetworkX:
907-
https://networkx.org/documentation/networkx-2.8.8/reference/classes/digraph.html</url>, <url>
908909
:WMA:
909910
https://reference.wolfram.com/language/ref/DirectedEdge.html</url>)
910911
@@ -921,6 +922,10 @@ class DirectedEdge(GenericDirectedEdge):
921922
= a → b
922923
"""
923924

925+
# attributes change from the default, so we need to
926+
# specify this below. Other things like "formats",
927+
# "operator" and "summary" do not change from the default.
928+
924929
attributes = A_PROTECTED | A_READ_PROTECTED
925930

926931

@@ -1088,7 +1093,9 @@ class FindShortestPath(_NetworkXBuiltin):
10881093

10891094
summary_text = "find the shortest path between two vertices"
10901095

1091-
def eval_s_t(self, graph, s, t, expression, evaluation, options):
1096+
def eval_s_t(
1097+
self, graph, s, t, expression: Expression, evaluation: Evaluation, options: dict
1098+
):
10921099
"FindShortestPath[graph_, s_, t_, OptionsPattern[FindShortestPath]]"
10931100
graph = self._build_graph(graph, evaluation, options, expression)
10941101
if not graph:
@@ -1507,7 +1514,9 @@ def _items(self, graph):
15071514

15081515
class UndirectedEdge(GenericUndirectedEdge):
15091516
"""
1510-
<url>
1517+
Edge of a <url>
1518+
:Undirected graph:
1519+
https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Undirected_graph</url> <url>
15111520
:WMA link:
15121521
https://reference.wolfram.com/language/ref/UndirectedEdge.html</url>
15131522
@@ -1520,6 +1529,10 @@ class UndirectedEdge(GenericUndirectedEdge):
15201529
= ...
15211530
"""
15221531

1532+
# attributes change from the default, so we need to
1533+
# specify this below. Other things like "formats",
1534+
# "operator" and "summary" do not change from the default.
1535+
15231536
attributes = A_PROTECTED | A_READ_PROTECTED
15241537

15251538

0 commit comments

Comments
 (0)