1111
1212from inspect import isgenerator
1313
14+
1415from mathics .builtin .base import AtomBuiltin , Builtin
1516from mathics .builtin .box .graphics import GraphicsBox
1617from mathics .builtin .box .inout import _BoxedString
1718from mathics .builtin .patterns import Matcher
1819from 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
2021from mathics .core .expression import Atom , Expression
21- from mathics .core .symbols import Symbol , SymbolTrue
22+ from mathics .core .symbols import Symbol , SymbolFalse , SymbolTrue
2223from mathics .core .systemsymbols import (
2324 SymbolBlank ,
2425 SymbolGraphics ,
7172SymbolDirectedEdge = Symbol ("DirectedEdge" )
7273SymbolCases = Symbol ("Cases" )
7374SymbolCases = Symbol ("DirectedEdge" )
75+ SymbolGraph = Symbol ("Graph" )
7476SymbolGraphBox = Symbol ("GraphBox" )
7577SymbolLength = Symbol ("Length" )
7678SymbolUndirectedEdge = 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
267301class 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):
783838class 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
794850class 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
928983class 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
9591012class 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
9831036class 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
10201074class 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
10471098class 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
10721123class 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
11081159class 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
11391190class 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
11721218class 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
0 commit comments