Skip to content

Commit 286c9ff

Browse files
committed
Align better with WMA
Graph Construction & Representation: https://reference.wolfram.com/language/guide/GraphConstructionAndRepresentation.html Move eval stuff to pymathics.graph.eval
1 parent cb9f847 commit 286c9ff

13 files changed

Lines changed: 368 additions & 325 deletions

File tree

pymathics/graph/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@
6161
WeaklyConnectedComponents,
6262
)
6363

64-
from pymathics.graph.generators import (
64+
from pymathics.graph.curated import GraphData
65+
66+
from pymathics.graph.parametric import (
6567
BalancedTree,
6668
BarbellGraph,
6769
BinomialTree,
@@ -70,17 +72,18 @@
7072
CycleGraph,
7173
FullRAryTree,
7274
GraphAtlas,
73-
GraphData,
7475
HknHararyGraph,
7576
HmnHararyGraph,
7677
KaryTree,
7778
LadderGraph,
78-
PathGraph,
79-
RandomGraph,
8079
RandomTree,
8180
StarGraph,
8281
)
83-
from pymathics.graph.tree import TreeGraphAtom, TreeGraph, TreeGraphQ
82+
83+
from pymathics.graph.random import RandomGraph
84+
85+
from pymathics.graph.structured import PathGraph, TreeGraph
86+
from pymathics.graph.tree import TreeGraphAtom, TreeGraphQ
8487
from pymathics.graph.version import __version__
8588

8689
pymathics_version_data = {

pymathics/graph/algorithms.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Algorithms on Graphs
44
"""
55

6+
import networkx as nx
7+
68
from typing import Optional
79

810
from mathics.core.atoms import Integer1, Integer2, Integer3
@@ -20,7 +22,6 @@
2022
SymbolUndirectedEdge,
2123
_create_graph,
2224
_NetworkXBuiltin,
23-
nx,
2425
)
2526

2627

@@ -89,7 +90,7 @@ class GraphDistance(_NetworkXBuiltin):
8990
9091
<dl>
9192
<dt>'GraphDistance[{$v$->$w$, ...}, ...]'
92-
<dd>use rules $v$->$w$ to specify the graph $g$
93+
<dd>use rules $v$->$w$ to specify the graph $g$.
9394
</dl>
9495
9596
>> GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 1, 5]

pymathics/graph/base.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
from collections import defaultdict
1313
from inspect import isgenerator
14+
from typing import Callable, Optional
1415

1516
from mathics.builtin.base import AtomBuiltin, Builtin
1617
from mathics.builtin.box.graphics import GraphicsBox
17-
from mathics.core.atoms import Atom, Integer, Integer0, Integer1, Integer2, Real
18+
from mathics.core.atoms import Atom, Integer, Integer0, Integer1, Integer2, Real, String
1819
from mathics.core.convert.expression import ListExpression, from_python
1920
from mathics.core.element import BaseElement
2021
from mathics.core.expression import Expression
@@ -80,6 +81,37 @@
8081
import networkx as nx
8182

8283

84+
def graph_helper(
85+
graph_generator_func: Callable,
86+
options: dict,
87+
can_digraph: bool,
88+
graph_layout: str,
89+
evaluation,
90+
root: Optional[int] = None,
91+
*args,
92+
**kwargs,
93+
) -> Optional[Callable]:
94+
should_digraph = can_digraph and has_directed_option(options)
95+
try:
96+
G = (
97+
graph_generator_func(*args, create_using=nx.DiGraph, **kwargs)
98+
if should_digraph
99+
else graph_generator_func(*args, **kwargs)
100+
)
101+
except MemoryError:
102+
evaluation.message("Graph", "mem", evaluation)
103+
return None
104+
if graph_layout and not options["System`GraphLayout"].get_string_value():
105+
options["System`GraphLayout"] = String(graph_layout)
106+
107+
g = Graph(G)
108+
_process_graph_options(g, options)
109+
110+
if root is not None:
111+
G.root = g.root = root
112+
return g
113+
114+
83115
def has_directed_option(options: dict) -> bool:
84116
return options.get("System`DirectedEdges", False).to_python()
85117

@@ -816,9 +848,7 @@ def track_edges(*edges):
816848
multigraph[0] = True
817849

818850
edge_weights = _edge_weights(options)
819-
use_directed_edges = (
820-
options.get("System`DirectedEdges", SymbolTrue) is SymbolTrue
821-
)
851+
use_directed_edges = options.get("System`DirectedEdges", SymbolTrue) is SymbolTrue
822852

823853
directed_edge_head = (
824854
SymbolDirectedEdge if use_directed_edges else SymbolUndirectedEdge
@@ -903,7 +933,7 @@ def full_new_edge_properties(new_edge_style):
903933
edge_properties = list(full_new_edge_properties(edge_options))
904934
for edge, attr_dict in zip(new_edges, edge_properties):
905935
parse_edge(edge, attr_dict)
906-
except _GraphParseError as e:
936+
except _GraphParseError:
907937
return None
908938

909939
empty_dict = {}
@@ -947,7 +977,9 @@ def full_new_edge_properties(new_edge_style):
947977

948978

949979
class _Centrality(_NetworkXBuiltin):
950-
options ={"WorkingPrecision": "MachinePrecision",}
980+
options = {
981+
"WorkingPrecision": "MachinePrecision",
982+
}
951983
pass
952984

953985

@@ -1320,8 +1352,9 @@ class EdgeRules(_NetworkXBuiltin):
13201352
<dd> gives the list of edge rules for the graph $g$.
13211353
</dl>
13221354
"""
1355+
13231356
summary_text = "list the edge rules"
1324-
1357+
13251358
def eval(self, graph, expression, evaluation, options):
13261359
"%(name)s[graph_, OptionsPattern[%(name)s]]"
13271360
graph = self._build_graph(graph, evaluation, options, expression)
@@ -1341,7 +1374,7 @@ class EigenvectorCentrality(_ComponentwiseCentrality):
13411374
https://en.wikipedia.org/wiki/Eigenvector_centrality</url> (<url>
13421375
:Networkx:
13431376
https://networkx.org/documentation/networkx-2.8.8/reference/algorithms\
1344-
/generated/networkx.algorithms.centrality.eigenvector_centrality.html</url>,
1377+
/generated/networkx.algorithms.centrality.eigenvector_centrality.html</url>,
13451378
<url>
13461379
:WMA:
13471380
https://reference.wolfram.com/language/ref/EgenvectorCentrality.html</url>)
@@ -1378,7 +1411,7 @@ class EigenvectorCentrality(_ComponentwiseCentrality):
13781411
"""
13791412

13801413
summary_text = "compute the eigenvector centralities"
1381-
1414+
13821415
def _centrality(self, g, weight):
13831416
return nx.eigenvector_centrality(g, max_iter=10000, tol=1.0e-7, weight=weight)
13841417

@@ -1466,7 +1499,7 @@ class FindVertexCut(_NetworkXBuiltin):
14661499
"""
14671500

14681501
summary_text = "find the vertex cuts"
1469-
1502+
14701503
def eval(self, graph, expression, evaluation, options):
14711504
"FindVertexCut[graph_, OptionsPattern[%(name)s]]"
14721505
graph = self._build_graph(graph, evaluation, options, expression)
@@ -1573,7 +1606,7 @@ class HITSCentrality(_Centrality):
15731606
https://en.wikipedia.org/wiki/HITS_centrality</url> (<url>
15741607
:Networkx:
15751608
https://networkx.org/documentation/networkx-2.8.8/reference/algorithms/\
1576-
generated/networkx.algorithms.link_analysis.hits_alg.hits.html</url>,
1609+
generated/networkx.algorithms.link_analysis.hits_alg.hits.html</url>,
15771610
<url>
15781611
:WMA:
15791612
https://reference.wolfram.com/language/ref/HITSCentrality.html</url>)
@@ -1652,7 +1685,7 @@ class KatzCentrality(_ComponentwiseCentrality):
16521685
<dl>
16531686
<dt>'KatzCentrality'[$g$, $alpha$]
16541687
<dd>gives a list of Katz centralities for the \
1655-
vertices in the graph $g$ and weight $alpha$.
1688+
vertices in the graph $g$ and weight $alpha$.
16561689
<dt>'KatzCentrality'[$g$, $alpha$, $beta$]
16571690
<dd>gives a list of Katz centralities for the \
16581691
vertices in the graph $g$ and weight $alpha$ and initial centralities $beta$.
@@ -1673,7 +1706,7 @@ class KatzCentrality(_ComponentwiseCentrality):
16731706
"""
16741707

16751708
summary_text = "Katz centrality"
1676-
1709+
16771710
rules = {
16781711
"Pymathics`KatzCentrality[Pymathics`g_, Pymathics`alpha_]": "Pymathics`KatzCentrality[Pymathics`g, Pymathics`alpha, 1]",
16791712
}
@@ -2073,7 +2106,7 @@ class UndirectedEdge(Builtin):
20732106
>> a <-> (b <-> c)
20742107
= UndirectedEdge[a, UndirectedEdge[b, c]]
20752108
"""
2076-
2109+
20772110
summary_text = "makes undirected graph edge"
20782111
pass
20792112

pymathics/graph/curated.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Curated Graphs
3+
"""
4+
5+
import networkx as nx
6+
7+
from mathics.core.evaluation import Evaluation
8+
9+
from pymathics.graph.base import (
10+
Graph,
11+
_NetworkXBuiltin,
12+
graph_helper,
13+
)
14+
15+
16+
class GraphData(_NetworkXBuiltin):
17+
"""
18+
<dl>
19+
<dt>'GraphData[$name$]'
20+
<dd>Returns a graph with the specified name.
21+
</dl>
22+
23+
>> GraphData["PappusGraph"]
24+
"""
25+
26+
def eval(self, name, expression, evaluation: Evaluation, options: dict) -> Graph:
27+
"GraphData[name_String, OptionsPattern[GraphData]]"
28+
py_name = name.get_string_value()
29+
fn, layout = WL_TO_NETWORKX_FN.get(py_name, (None, None))
30+
if not fn:
31+
if not py_name.endswith("_graph"):
32+
py_name += "_graph"
33+
if py_name in ("LCF_graph", "make_small_graph"):
34+
# These graphs require parameters
35+
return
36+
import inspect
37+
38+
fn = dict(inspect.getmembers(nx, inspect.isfunction)).get(py_name, None)
39+
# parameters = inspect.signature(nx.diamond_graph).parameters.values()
40+
# if len([p for p in list(parameters) if p.kind in [inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD]]) != 0:
41+
# return
42+
if fn:
43+
g = graph_helper(fn, options, False, evaluation, layout)
44+
g.G.name = py_name
45+
return g
46+
47+
48+
WL_TO_NETWORKX_FN = {
49+
"DodecahedralGraph": (nx.dodecahedral_graph, None),
50+
"DiamondGraph": (nx.diamond_graph, "spring"),
51+
"PappusGraph": (nx.pappus_graph, "circular"),
52+
"IsohedralGraph": (nx.icosahedral_graph, "spring"),
53+
"PetersenGraph": (nx.petersen_graph, None),
54+
}
55+
56+
# TODO: ExampleData

pymathics/graph/eval/__init__.py

Whitespace-only changes.
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import networkx as nx
2020
from networkx.exception import NetworkXError
2121

22-
__all__ = ["hnm_harary_graph", "hkn_harary_graph"]
23-
2422

2523
def hnm_harary_graph(n, m, create_using=None):
2624
"""Returns the Harary graph with given numbers of nodes and edges.

pymathics/graph/eval/parametric.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
evaluation methods for Parametric Graphs
3+
"""
4+
5+
import networkx as nx
6+
from typing import Optional
7+
8+
from mathics.core.atoms import Integer
9+
from mathics.core.evaluation import Evaluation
10+
from pymathics.graph.base import Graph, graph_helper
11+
from pymathics.graph.eval.harary import hkn_harary_graph
12+
13+
14+
def eval_complete_graph(
15+
self, n: Integer, expression, evaluation: Evaluation, options: dict
16+
) -> Optional[Graph]:
17+
py_n = n.value
18+
19+
if py_n < 1:
20+
evaluation.message(self.get_name(), "ilsmp", expression)
21+
return
22+
23+
args = (py_n,)
24+
g = graph_helper(
25+
nx.complete_graph, options, False, "circular", evaluation, None, *args
26+
)
27+
if not g:
28+
return None
29+
30+
g.G.n = n
31+
return g
32+
33+
34+
def eval_full_rary_tree(
35+
self, r: Integer, n: Integer, expression, evaluation: Evaluation, options: dict
36+
) -> Optional[Graph]:
37+
"""
38+
Call networkx to get a full_raray_tree using parameters, ``r`` and ``t``.
39+
"""
40+
py_r = r.value
41+
42+
if py_r < 0:
43+
evaluation.message(self.get_name(), "ilsmp", expression)
44+
return
45+
46+
py_n = n.value
47+
if py_n < 0:
48+
evaluation.message(self.get_name(), "ilsmp", expression)
49+
return
50+
51+
args = (py_r, py_n)
52+
g = graph_helper(nx.full_rary_tree, options, True, "tree", evaluation, 0, *args)
53+
if not g:
54+
return None
55+
56+
g.G.r = r
57+
g.G.n = n
58+
return g
59+
60+
61+
def eval_hkn_harary(
62+
self, k: Integer, n: Integer, expression, evaluation: Evaluation, options: dict
63+
) -> Optional[Graph]:
64+
py_k = k.value
65+
66+
if py_k < 0:
67+
evaluation.message(self.get_name(), "ilsmp", expression)
68+
return
69+
70+
py_n = n.value
71+
if py_n < 0:
72+
evaluation.message(self.get_name(), "ilsmp2", expression)
73+
return
74+
75+
args = (py_k, py_n)
76+
g = graph_helper(
77+
hkn_harary_graph, options, False, "circular", evaluation, None, *args
78+
)
79+
if not g:
80+
return None
81+
g.k = py_k
82+
g.n = py_n
83+
return g

0 commit comments

Comments
 (0)