Skip to content

Commit 32c16e5

Browse files
authored
Merge pull request #9 from Mathics3/test-adjustments
Pull out pytests from algorithms doctest
2 parents 9563ccc + 96736f2 commit 32c16e5

File tree

6 files changed

+185
-35
lines changed

6 files changed

+185
-35
lines changed

pymathics/graph/__init__.py

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Graphs - Vertices and Edges
33
4-
A Pymathics module that provides functions and variables for working with graphs.
4+
Mathics3 Module that provides functions and variables for working with graphs.
55
66
Example:
77
In[1]:= LoadModule["pymathics.graph"]
@@ -52,14 +52,105 @@
5252

5353
from pymathics.graph.measures_and_metrics import EdgeCount, VertexCount, VertexDegree
5454

55-
from pymathics.graph.algorithms import * # noqa
56-
from pymathics.graph.generators import * # noqa
57-
from pymathics.graph.tree import * # noqa
58-
from pymathics.graph.version import __version__ # noqa
55+
from pymathics.graph.algorithms import (
56+
ConnectedComponents,
57+
GraphDistance,
58+
FindSpanningTree,
59+
PlanarGraphQ,
60+
WeaklyConnectedComponents,
61+
)
62+
63+
from pymathics.graph.generators import (
64+
BalancedTree,
65+
BarbellGraph,
66+
BinomialTree,
67+
CompleteGraph,
68+
CompleteKaryTree,
69+
CycleGraph,
70+
FullRAryTree,
71+
GraphAtlas,
72+
HknHararyGraph,
73+
HmnHararyGraph,
74+
KaryTree,
75+
LadderGraph,
76+
PathGraph,
77+
RandomGraph,
78+
RandomTree,
79+
StarGraph,
80+
)
81+
from pymathics.graph.tree import TreeGraphAtom, TreeGraph, TreeGraphQ
82+
from pymathics.graph.version import __version__
5983

6084
pymathics_version_data = {
6185
"author": "The Mathics3 Team",
6286
"version": __version__,
6387
"name": "Graph",
6488
"requires": ["networkx"],
6589
}
90+
91+
# Thsee are the publicly exported names
92+
__all__ = [
93+
"AcyclicGraphQ",
94+
"BalancedTree",
95+
"BarbellGraph",
96+
"BetweennessCentrality",
97+
"BinomialTree",
98+
"ClosenessCentrality",
99+
"CompleteGraph",
100+
"CompleteKaryTree",
101+
"ConnectedComponents",
102+
"ConnectedGraphQ",
103+
"CycleGraph",
104+
"DegreeCentrality",
105+
"DirectedEdge",
106+
"DirectedGraphQ",
107+
"EdgeConnectivity",
108+
"EdgeCount",
109+
"EdgeIndex",
110+
"EdgeList",
111+
"EdgeRules",
112+
"EigenvectorCentrality",
113+
"FindShortestPath",
114+
"FindSpanningTree",
115+
"FindVertexCut",
116+
"FullRAryTree",
117+
"Graph",
118+
"GraphAtlas",
119+
"GraphAtom",
120+
"GraphBox",
121+
"GraphDistance",
122+
"HITSCentrality",
123+
"HighlightGraph",
124+
"HknHararyGraph",
125+
"HmnHararyGraph",
126+
"KaryTree",
127+
"KatzCentrality",
128+
"LadderGraph",
129+
"LoopFreeGraphQ",
130+
"MixedGraphQ",
131+
"MultigraphQ",
132+
"PageRankCentrality",
133+
"PathGraph",
134+
"PathGraphQ",
135+
"PlanarGraphQ",
136+
"Property",
137+
"PropertyValue",
138+
"RandomGraph",
139+
"RandomTree",
140+
"SimpleGraphQ",
141+
"StarGraph",
142+
"TreeGraph",
143+
"TreeGraphAtom",
144+
"TreeGraphQ",
145+
"UndirectedEdge",
146+
"VertexAdd",
147+
"VertexConnectivity",
148+
"VertexCount",
149+
"VertexDegree",
150+
"VertexDelete",
151+
"VertexIndex",
152+
"VertexList",
153+
"WeaklyConnectedComponents",
154+
"__version__",
155+
"pymathics_version_data",
156+
]

pymathics/graph/algorithms.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class ConnectedComponents(_NetworkXBuiltin):
3232
</dl>
3333
3434
>> g = Graph[{1 -> 2, 2 -> 3, 3 <-> 4}]; ConnectedComponents[g]
35-
= {{3, 4}, {2}, {1}}
35+
= ...
3636
3737
>> g = Graph[{1 -> 2, 2 -> 3, 3 -> 1}]; ConnectedComponents[g]
38-
= {{1, 2, 3}}
38+
= ...
3939
4040
>> g = Graph[{1 <-> 2, 2 <-> 3, 3 -> 4, 4 <-> 5}]; ConnectedComponents[g]
41-
= {{4, 5}, {1, 2, 3}}
41+
= ...
4242
"""
4343

4444
def eval(
@@ -102,14 +102,11 @@ class GraphDistance(_NetworkXBuiltin):
102102
= Infinity
103103
104104
>> GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 3]
105-
= {2, 1, 0, 1, 2}
105+
= ...
106106
107107
>> GraphDistance[{1 <-> 2, 3 <-> 4}, 3]
108108
= {Infinity, Infinity, 0, 1}
109109
110-
#> GraphDistance[{}, 1, 1]
111-
: The vertex at position 2 in GraphDistance[{}, 1, 1] does not belong to the graph at position 1.
112-
= GraphDistance[{}, 1, 1]
113110
#> GraphDistance[{1 -> 2}, 3, 4]
114111
: The vertex at position 2 in GraphDistance[{1 -> 2}, 3, 4] does not belong to the graph at position 1.
115112
= GraphDistance[{1 -> 2}, 3, 4]
@@ -203,7 +200,7 @@ class PlanarGraphQ(_NetworkXBuiltin):
203200
#> PlanarGraphQ[Graph[{}]]
204201
= False
205202
206-
203+
207204
>> PlanarGraphQ["abc"]
208205
: Expected a graph at position 1 in PlanarGraphQ[abc].
209206
= False

pymathics/graph/base.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
)
3232
from mathics.eval.makeboxes import _boxed_string
3333
from mathics.eval.patterns import Matcher
34+
from pymathics.graph.graphsymbols import SymbolDirectedEdge, SymbolUndirectedEdge
35+
3436

3537
WL_MARKER_TO_NETWORKX = {
3638
"Circle": "o",
@@ -72,11 +74,6 @@
7274

7375
import networkx as nx
7476

75-
SymbolDirectedEdge = Symbol("DirectedEdge")
76-
SymbolGraph = Symbol("Graph")
77-
SymbolGraphBox = Symbol("GraphBox")
78-
SymbolUndirectedEdge = Symbol("UndirectedEdge")
79-
8077

8178
def has_directed_option(options: dict) -> bool:
8279
return options.get("System`DirectedEdges", False).to_python()
@@ -627,7 +624,7 @@ def do_format(self, evaluation, form):
627624
return self
628625

629626
@property
630-
def edges(self)->tuple:
627+
def edges(self) -> tuple:
631628
# TODO: check if this should not return expressions
632629
return self.G.edges
633630

@@ -674,7 +671,7 @@ def get_sort_key(self, pattern_sort=False) -> tuple:
674671

675672
def sort_vertices(self, vertices):
676673
return sorted(vertices)
677-
674+
678675
def update_weights(self, evaluation):
679676
weights = None
680677
G = self.G
@@ -733,6 +730,7 @@ def _create_graph(
733730
known_vertices = set()
734731
vertices = []
735732
vertex_properties = []
733+
736734
def add_vertex(x, attr_dict=None):
737735
if x.has_form("Property", 2):
738736
expr, prop = x.elements
@@ -754,7 +752,7 @@ def add_vertex(x, attr_dict=None):
754752
old_vertices = dict(from_graph.nodes.data())
755753
vertices += old_vertices
756754
edges = list(from_graph.edges.data())
757-
755+
758756
for edge, attr_dict in edges:
759757
u, v = edge.elements
760758
if edge.get_head_name() == "System`DirectedEdge":
@@ -792,13 +790,17 @@ def track_edges(*edges):
792790

793791
def parse_edge(r, attr_dict):
794792
if isinstance(r, Atom):
795-
raise _GraphParseError(msg = f"{r} is an atom, and hence does not define an edge.")
793+
raise _GraphParseError(
794+
msg=f"{r} is an atom, and hence does not define an edge."
795+
)
796796

797797
name = r.get_head_name()
798798
elements = r.elements
799799

800800
if len(elements) != 2:
801-
raise _GraphParseError(msg = f"{r} does not have 2 elements, so it is not an edge.")
801+
raise _GraphParseError(
802+
msg=f"{r} does not have 2 elements, so it is not an edge."
803+
)
802804

803805
u, v = elements
804806
assert isinstance(u, BaseElement) and isinstance(v, BaseElement)
@@ -827,7 +829,7 @@ def parse_edge(r, attr_dict):
827829
pass
828830
pass
829831
else:
830-
raise _GraphParseError(msg = f"{name} is an unknown kind of edge.")
832+
raise _GraphParseError(msg=f"{name} is an unknown kind of edge.")
831833

832834
if head.get_name() == name:
833835
edges.append(r)
@@ -1514,7 +1516,7 @@ def eval(self, graph, expression, evaluation, options):
15141516
tol = 1.0e-14
15151517
_, a = nx.hits(G, normalized=True, tol=tol)
15161518
h, _ = nx.hits(G, normalized=False, tol=tol)
1517-
1519+
15181520
def _crop(x):
15191521
return 0 if x < tol else x
15201522

@@ -1738,7 +1740,6 @@ def eval(self, graph, expression, evaluation, options):
17381740
return from_python(is_path)
17391741

17401742

1741-
17421743
class Property(Builtin):
17431744
pass
17441745

pymathics/graph/graphsymbols.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from mathics.core.symbols import Symbol
2+
3+
SymbolDirectedEdge = Symbol("DirectedEdge")
4+
SymbolGraph = Symbol("Graph")
5+
SymbolGraphBox = Symbol("GraphBox")
6+
SymbolUndirectedEdge = Symbol("UndirectedEdge")

test/test_algorithms.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,44 @@ def setup_module(module):
1313

1414
def test_connected_components():
1515
for str_expr, str_expected in [
16-
("PlanarGraphQ[Graph[{}]]", "False"),
17-
("g = Graph[{1 -> 2, 2 -> 3, 3 <-> 4}];", "Null"),
18-
("SortList[ConnectedComponents[g]]", "{{1}, {2}, {3, 4}}"),
19-
("g = Graph[{1 -> 2, 2 -> 3, 3 -> 1}];", "Null"),
20-
("SortList[ConnectedComponents[g]]", "{{1, 2, 3}}"),
16+
("PlanarGraphQ[Graph[{}]]", "False"),
17+
("g = Graph[{1 -> 2, 2 -> 3, 3 <-> 4}];", "Null"),
18+
("SortList[ConnectedComponents[g]]", "{{1}, {2}, {3, 4}}"),
19+
("g = Graph[{1 -> 2, 2 -> 3, 3 -> 1}];", "Null"),
20+
("SortList[ConnectedComponents[g]]", "{{1, 2, 3}}"),
2121
]:
2222
check_evaluation(str_expr, str_expected)
2323

2424

2525
def test_graph_distance():
26-
for str_expr, str_expected in [
27-
("GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 1, 5]", "3"),
28-
("GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 4 -> 2, 4 -> 5}, 1, 5]", "4"),
26+
for str_expr, str_expected, mess in [
27+
(
28+
"GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 1, 5]",
29+
"3",
30+
None,
31+
),
32+
("GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 4 -> 2, 4 -> 5}, 1, 5]", "4", None),
2933
# ("GraphDistance[{1 <-> 2, 2 <-> 3, 4 -> 3, 4 -> 2, 4 -> 5}, 1, 5]", "Infinity"),
3034
(
3135
"Sort[GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 3]]",
3236
"{0, 1, 1, 2, 2}",
37+
None,
38+
),
39+
(
40+
"GraphDistance[{}, 1, 1]",
41+
"GraphDistance[{}, 1, 1]",
42+
[
43+
"The vertex at position 2 in GraphDistance[{}, 1, 1] does not belong to "
44+
"the graph at position 1."
45+
],
46+
),
47+
(
48+
"GraphDistance[{1 -> 2}, 3, 4]",
49+
"GraphDistance[{1 -> 2}, 3, 4]",
50+
[
51+
"The vertex at position 2 in GraphDistance[{1 -> 2}, 3, 4] does not belong "
52+
"to the graph at position 1."
53+
],
3354
),
3455
]:
35-
check_evaluation(str_expr, str_expected)
56+
check_evaluation(str_expr, str_expected, expected_messages=mess)

test/test_generators.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Unit tests for mathics.builtins.numbers.algebra
4+
"""
5+
from test.helper import check_evaluation, evaluate, evaluate_value
6+
7+
8+
def setup_module(module):
9+
"""Load pymathics.graph"""
10+
assert evaluate_value('LoadModule["pymathics.graph"]') == "pymathics.graph"
11+
evaluate("SortList[list_] := Sort[Map[Sort, list]]")
12+
13+
14+
def test_connected_components():
15+
for str_expr, str_expected in [
16+
("g = Graph[{1 -> 2, 2 -> 3, 3 <-> 4}];", "Null"),
17+
("SortList[ConnectedComponents[g]]", "{{1}, {2}, {3, 4}}"),
18+
("g = Graph[{1 -> 2, 2 -> 3, 3 -> 1}];", "Null"),
19+
("SortList[ConnectedComponents[g]]", "{{1, 2, 3}}"),
20+
]:
21+
check_evaluation(str_expr, str_expected)
22+
23+
24+
def test_graph_distance():
25+
for str_expr, str_expected in [
26+
("GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 1, 5]", "3"),
27+
("GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 4 -> 2, 4 -> 5}, 1, 5]", "4"),
28+
# ("GraphDistance[{1 <-> 2, 2 <-> 3, 4 -> 3, 4 -> 2, 4 -> 5}, 1, 5]", "Infinity"),
29+
(
30+
"Sort[GraphDistance[{1 <-> 2, 2 <-> 3, 3 <-> 4, 2 <-> 4, 4 -> 5}, 3]]",
31+
"{0, 1, 1, 2, 2}",
32+
),
33+
]:
34+
check_evaluation(str_expr, str_expected)

0 commit comments

Comments
 (0)