Skip to content

Commit dd62dc6

Browse files
committed
More massive changes to get more of this working
1 parent b52792a commit dd62dc6

File tree

2 files changed

+118
-72
lines changed

2 files changed

+118
-72
lines changed

pymathics/graph/__main__.py

Lines changed: 116 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111

1212
from inspect import isgenerator
1313

14+
1415
from mathics.builtin.base import AtomBuiltin, Builtin
1516
from mathics.builtin.box.graphics import GraphicsBox
1617
from mathics.builtin.box.inout import _BoxedString
1718
from mathics.builtin.patterns import Matcher
1819
from mathics.core.atoms import Integer, Integer0, Integer1, Real
19-
from mathics.core.convert.expression import ListExpression
20+
from mathics.core.convert.expression import ListExpression, from_python
2021
from mathics.core.expression import Atom, Expression
21-
from mathics.core.symbols import Symbol, SymbolTrue
22+
from mathics.core.symbols import Symbol, SymbolFalse, SymbolTrue
2223
from mathics.core.systemsymbols import (
2324
SymbolBlank,
2425
SymbolGraphics,
@@ -71,6 +72,7 @@
7172
SymbolDirectedEdge = Symbol("DirectedEdge")
7273
SymbolCases = Symbol("Cases")
7374
SymbolCases = Symbol("DirectedEdge")
75+
SymbolGraph = Symbol("Graph")
7476
SymbolGraphBox = Symbol("GraphBox")
7577
SymbolLength = Symbol("Length")
7678
SymbolUndirectedEdge = Symbol("UndirectedEdge")
@@ -246,7 +248,11 @@ def _not_an_edge(self, expression, pos, evaluation):
246248

247249
def _build_graph(self, graph, evaluation, options, expr, quiet=False):
248250
head = graph.get_head_name()
249-
if head == "System`Graph" and isinstance(graph, Atom) and hasattr(graph, "G"):
251+
if (
252+
head == "Pymathics`Graph"
253+
and isinstance(graph, Atom)
254+
and hasattr(graph, "G")
255+
):
250256
return graph
251257
elif head == "System`List":
252258
return _graph_from_list(graph.elements, options)
@@ -263,6 +269,34 @@ def _evaluate_atom(self, graph, options, compute):
263269
def __str__(self):
264270
return "-Graph-"
265271

272+
# FIXME: return type should be a specific kind of Tuple, not a list.
273+
def get_sort_key(self, pattern_sort=False) -> list:
274+
"""
275+
Returns a particular encoded list (which should be a tuple) that is used
276+
in ``Sort[]`` comparisons and in the ordering that occurs
277+
in an M-Expression which has the ``Orderless`` property.
278+
279+
See the docstring for element.get_sort_key() for more detail.
280+
"""
281+
282+
if pattern_sort:
283+
return super(_NetworkXBuiltin, self).get_sort_key(True)
284+
else:
285+
# Return a sort_key tuple.
286+
# but with a `2` instead of `1` in the 5th position,
287+
# and adding two extra fields: the length in the 5th position,
288+
# and a hash in the 6th place.
289+
return [
290+
1,
291+
3,
292+
self.class_head_name,
293+
tuple(),
294+
2,
295+
len(self.vertices),
296+
hash(self),
297+
]
298+
return hash(self)
299+
266300

267301
class GraphBox(GraphicsBox):
268302
def _graphics_box(self, elements, options):
@@ -285,7 +319,7 @@ def boxes_to_tex(self, elements, **options):
285319
return "-Graph-TeX-"
286320

287321

288-
class _Collection(object):
322+
class _Collection:
289323
def __init__(self, expressions, properties=None, index=None):
290324
self.expressions = expressions
291325
self.properties = properties if properties else None
@@ -437,6 +471,33 @@ def default_format(self, evaluation, form):
437471
def do_format(self, evaluation, form):
438472
return self
439473

474+
def get_sort_key(self, pattern_sort=False) -> list:
475+
"""
476+
Returns a particular encoded list (which should be a tuple) that is used
477+
in ``Sort[]`` comparisons and in the ordering that occurs
478+
in an M-Expression which has the ``Orderless`` property.
479+
480+
See the docstring for element.get_sort_key() for more detail.
481+
"""
482+
483+
if pattern_sort:
484+
return super(Graph, self).get_sort_key(True)
485+
else:
486+
# Return a sort_key tuple.
487+
# but with a `2` instead of `1` in the 5th position,
488+
# and adding two extra fields: the length in the 5th position,
489+
# and a hash in the 6th place.
490+
return [
491+
1,
492+
3,
493+
self.class_head_name,
494+
tuple(),
495+
2,
496+
len(self.vertices),
497+
hash(self),
498+
]
499+
return hash(self)
500+
440501
@property
441502
def edges(self):
442503
return self.G.edges
@@ -455,12 +516,6 @@ def is_mixed_graph(self):
455516
def is_multigraph(self):
456517
return isinstance(self.G, (nx.MultiDiGraph, nx.MultiGraph))
457518

458-
def get_sort_key(self, pattern_sort=False):
459-
if pattern_sort:
460-
return super(Graph, self).get_sort_key(True)
461-
else:
462-
return hash(self)
463-
464519
@property
465520
def value(self):
466521
return self.G
@@ -470,7 +525,7 @@ def vertices(self):
470525
return self.G.nodes
471526

472527

473-
class _Collection(object):
528+
class _Collection:
474529
def __init__(self, expressions, properties=None, index=None):
475530
self.expressions = expressions
476531
self.properties = properties if properties else None
@@ -783,19 +838,20 @@ def apply(self, graph, item, name, evaluation):
783838
class DirectedEdge(Builtin):
784839
"""
785840
<dl>
786-
<dt>'DirectedEdge[$u$, $v$]'
787-
<dd>a directed edge from $u$ to $v$.
841+
<dt>'DirectedEdge[$u$, $v$]'
842+
<dd>create a directed edge from $u$ to $v$.
788843
</dl>
789844
"""
790845

846+
summary_text = "make a directed graph edge"
791847
pass
792848

793849

794850
class UndirectedEdge(Builtin):
795851
"""
796852
<dl>
797853
<dt>'UndirectedEdge[$u$, $v$]'
798-
<dd>an undirected edge between $u$ and $v$.
854+
<dd>create an undirected edge between $u$ and $v$.
799855
</dl>
800856
801857
>> a <-> b
@@ -808,6 +864,8 @@ class UndirectedEdge(Builtin):
808864
= UndirectedEdge[a, UndirectedEdge[b, c]]
809865
"""
810866

867+
summary_text = "makes undirected graph edge"
868+
811869
pass
812870

813871

@@ -904,25 +962,22 @@ class PathGraphQ(_NetworkXBuiltin):
904962

905963
def apply(self, graph, expression, evaluation, options):
906964
"PathGraphQ[graph_, OptionsPattern[%(name)s]]"
907-
if not isinstance(graph, Graph):
908-
return Symbol("False")
965+
if not isinstance(graph, Graph) or graph.empty():
966+
return SymbolFalse
909967

910-
if graph.empty():
911-
is_path = False
912-
else:
913-
G = graph.G
968+
G = graph.G
914969

915-
if G.is_directed():
916-
connected = nx.is_semiconnected(G)
917-
else:
918-
connected = nx.is_connected(G)
970+
if G.is_directed():
971+
connected = nx.is_semiconnected(G)
972+
else:
973+
connected = nx.is_connected(G)
919974

920-
if connected:
921-
is_path = all(d <= 2 for _, d in G.degree(graph.vertices))
922-
else:
923-
is_path = False
975+
if connected:
976+
is_path = all(d <= 2 for _, d in G.degree(graph.vertices))
977+
else:
978+
is_path = False
924979

925-
return Symbol("True" if is_path else "False")
980+
return from_python(is_path)
926981

927982

928983
class MixedGraphQ(_NetworkXBuiltin):
@@ -951,9 +1006,7 @@ def apply(self, graph, expression, evaluation, options):
9511006
"%(name)s[graph_, OptionsPattern[%(name)s]]"
9521007
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
9531008
if graph:
954-
return Symbol("True" if graph.is_mixed_graph() else "False")
955-
else:
956-
return Symbol("False")
1009+
return from_python(graph.is_mixed_graph())
9571010

9581011

9591012
class MultigraphQ(_NetworkXBuiltin):
@@ -975,9 +1028,9 @@ def apply(self, graph, expression, evaluation, options):
9751028
"%(name)s[graph_, OptionsPattern[%(name)s]]"
9761029
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
9771030
if graph:
978-
return Symbol("True" if graph.is_multigraph() else "False")
1031+
return from_python(graph.is_multigraph())
9791032
else:
980-
return Symbol("False")
1033+
return SymbolFalse
9811034

9821035

9831036
class AcyclicGraphQ(_NetworkXBuiltin):
@@ -1002,19 +1055,20 @@ class AcyclicGraphQ(_NetworkXBuiltin):
10021055
10031056
#> AcyclicGraphQ["abc"]
10041057
= False
1058+
: Expected a graph at position 1 in AcyclicGraphQ[abc].
10051059
"""
10061060

10071061
def apply(self, graph, expression, evaluation, options):
10081062
"%(name)s[graph_, OptionsPattern[%(name)s]]"
1009-
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
1010-
if graph and not graph.empty():
1011-
try:
1012-
cycles = nx.find_cycle(graph.G)
1013-
except nx.exception.NetworkXNoCycle:
1014-
cycles = None
1015-
return Symbol("True" if not cycles else "False")
1016-
else:
1017-
return Symbol("False")
1063+
graph = self._build_graph(graph, evaluation, options, expression, quiet=False)
1064+
if not graph or graph.empty():
1065+
return SymbolFalse
1066+
1067+
try:
1068+
cycles = nx.find_cycle(graph.G)
1069+
except nx.exception.NetworkXNoCycle:
1070+
return SymbolTrue
1071+
return from_python(not cycles)
10181072

10191073

10201074
class LoopFreeGraphQ(_NetworkXBuiltin):
@@ -1035,13 +1089,10 @@ class LoopFreeGraphQ(_NetworkXBuiltin):
10351089
def apply(self, graph, expression, evaluation, options):
10361090
"%(name)s[graph_, OptionsPattern[%(name)s]]"
10371091
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
1038-
if graph:
1039-
if graph.empty():
1040-
return Symbol("False")
1041-
else:
1042-
return Symbol("True" if graph.is_loop_free() else "False")
1043-
else:
1044-
return Symbol("False")
1092+
if not graph or graph.empty():
1093+
return SymbolFalse
1094+
1095+
return from_python(graph.is_loop_free())
10451096

10461097

10471098
class DirectedGraphQ(_NetworkXBuiltin):
@@ -1064,9 +1115,9 @@ def apply(self, graph, expression, evaluation, options):
10641115
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
10651116
if graph:
10661117
directed = graph.G.is_directed() and not graph.is_mixed_graph()
1067-
return Symbol("True" if directed else "False")
1118+
return from_python(directed)
10681119
else:
1069-
return Symbol("False")
1120+
return SymbolFalse
10701121

10711122

10721123
class ConnectedGraphQ(_NetworkXBuiltin):
@@ -1100,9 +1151,9 @@ def apply(self, graph, expression, evaluation, options):
11001151
"%(name)s[graph_, OptionsPattern[%(name)s]]"
11011152
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
11021153
if graph:
1103-
return Symbol("True" if _is_connected(graph.G) else "False")
1154+
return from_python(_is_connected(graph.G))
11041155
else:
1105-
return Symbol("False")
1156+
return SymbolFalse
11061157

11071158

11081159
class SimpleGraphQ(_NetworkXBuiltin):
@@ -1128,17 +1179,17 @@ def apply(self, graph, expression, evaluation, options):
11281179
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
11291180
if graph:
11301181
if graph.empty():
1131-
return Symbol("True")
1182+
return SymbolTrue
11321183
else:
11331184
simple = graph.is_loop_free() and not graph.is_multigraph()
1134-
return Symbol("True" if simple else "False")
1185+
return from_python(simple)
11351186
else:
1136-
return Symbol("False")
1187+
return SymbolFalse
11371188

11381189

11391190
class PlanarGraphQ(_NetworkXBuiltin):
11401191
"""
1141-
# see https://en.wikipedia.org/wiki/Planar_graph
1192+
See <url>https://en.wikipedia.org/wiki/Planar_graph</url>
11421193
11431194
>> PlanarGraphQ[CompleteGraph[4]]
11441195
= True
@@ -1153,20 +1204,15 @@ class PlanarGraphQ(_NetworkXBuiltin):
11531204
= False
11541205
"""
11551206

1156-
requires = _NetworkXBuiltin.requires + ("planarity",)
1207+
requires = _NetworkXBuiltin.requires
11571208

11581209
def apply(self, graph, expression, evaluation, options):
11591210
"%(name)s[graph_, OptionsPattern[%(name)s]]"
11601211
graph = self._build_graph(graph, evaluation, options, expression, quiet=True)
1161-
if graph:
1162-
if graph.empty():
1163-
return Symbol("False")
1164-
else:
1165-
import planarity
1212+
if not graph or graph.empty():
1213+
return SymbolFalse
11661214

1167-
return Symbol("True" if planarity.is_planar(graph.G) else "False")
1168-
else:
1169-
return Symbol("False")
1215+
return from_python(nx.is_planar(graph.G))
11701216

11711217

11721218
class FindVertexCut(_NetworkXBuiltin):
@@ -1359,7 +1405,7 @@ class AdjacencyList(_NetworkXBuiltin):
13591405
= {1, 3}
13601406
13611407
>> AdjacencyList[{x -> 2, x -> 3, x -> 4, 2 -> 10, 2 -> 11, 4 -> 20, 4 -> 21, 10 -> 100}, 10, 2]
1362-
= {x, 2, 11, 100}
1408+
= {2, 11, 100, x}
13631409
"""
13641410

13651411
def _retrieve(self, graph, what, neighbors, expression, evaluation):
@@ -1371,9 +1417,9 @@ def _retrieve(self, graph, what, neighbors, expression, evaluation):
13711417
for v in graph.G.nodes:
13721418
if match(v, evaluation):
13731419
collected.update(neighbors(v))
1374-
return ListExpression(*graph.sort_vertices(list(collected)))
1420+
return ListExpression(*sorted(collected))
13751421
elif graph.G.has_node(what):
1376-
return ListExpression(*graph.sort_vertices(neighbors(what)))
1422+
return ListExpression(*sorted(neighbors(what)))
13771423
else:
13781424
self._not_a_vertex(expression, 2, evaluation)
13791425

pymathics/graph/generators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,13 @@ class BarbellGraph(_NetworkXBuiltin):
121121

122122
def apply(self, m1, m2, expression, evaluation, options):
123123
"%(name)s[m1_Integer, m2_Integer, OptionsPattern[%(name)s]]"
124-
py_m1 = m1.get_int_value()
124+
py_m1 = m1.value
125125

126126
if py_m1 < 0:
127127
evaluation.message(self.get_name(), "ilsmp", expression)
128128
return
129129

130-
py_m2 = m2.get_int_value()
130+
py_m2 = m2.value
131131
if py_m2 < 0:
132132
evaluation.message(self.get_name(), "ilsmp", expression)
133133
return

0 commit comments

Comments
 (0)