88
99from collections import defaultdict
1010from inspect import isgenerator
11- from typing import Callable , Optional
11+ from typing import Callable , Optional , Union
1212
1313from mathics .builtin .base import AtomBuiltin , Builtin
1414from mathics .core .atoms import Atom , Integer , Integer0 , Integer1 , Integer2 , String
15- from mathics .core .convert .expression import ListExpression , from_python
15+ from mathics .core .convert .expression import ListExpression , from_python , to_mathics_list
1616from mathics .core .element import BaseElement
1717from mathics .core .expression import Expression
1818from mathics .core .symbols import Symbol , SymbolList , SymbolTrue
@@ -86,7 +86,18 @@ def graph_helper(
8686 root : Optional [int ] = None ,
8787 * args ,
8888 ** kwargs ,
89- ) -> Optional [Callable ]:
89+ ) -> Optional ["Graph" ]:
90+ """
91+ This is a wrapping function for a NetworkX function of some sort indicated in ``graph_generator``,
92+ e.g. ``nx.complete_graph``.
93+
94+ ``args`` and ``kwargs`` are passed to the NetworkX function, while ``can_digraph`` determines
95+ whether ``create_using=nx.DiGraph``is added to the NetworkX graph-generation function call.
96+
97+ Parameter ``options`` and ``graph_layout`` are Mathics3 Graph
98+ options; ``graph_layout`` has a ``String()`` wrapped around it
99+ ``root`` is used when the graph is a tree.
100+ """
90101 should_digraph = can_digraph and has_directed_option (options )
91102 try :
92103 G = (
@@ -97,7 +108,11 @@ def graph_helper(
97108 except MemoryError :
98109 evaluation .message ("Graph" , "mem" , evaluation )
99110 return None
100- if graph_layout and not options ["System`GraphLayout" ].get_string_value ():
111+ if (
112+ graph_layout and not options ["System`GraphLayout" ].get_string_value ()
113+ if "System`GraphLayout" in options
114+ else False
115+ ):
101116 options ["System`GraphLayout" ] = String (graph_layout )
102117
103118 g = Graph (G )
@@ -279,7 +294,14 @@ def _not_a_vertex(self, expression, pos, evaluation):
279294 def _not_an_edge (self , expression , pos , evaluation ):
280295 evaluation .message (self .get_name (), "inv" , "edge" , pos , expression )
281296
282- def _build_graph (self , graph , evaluation , options , expr , quiet = False ):
297+ def _build_graph (
298+ self ,
299+ graph : Union ["Graph" , Expression ],
300+ evaluation ,
301+ options : dict ,
302+ expr ,
303+ quiet = False ,
304+ ) -> Optional ["Graph" ]:
283305 head = graph .get_head ()
284306 if head is SymbolGraph and isinstance (graph , Atom ) and hasattr (graph , "G" ):
285307 return graph
@@ -380,6 +402,16 @@ def __init__(self, Gr, **kwargs):
380402 self .G = Gr
381403 self .mixed = kwargs .get ("mixed" , False )
382404
405+ # Here we define types that appear on some, but not all
406+ # graphs. So these are optional, which we given an initial
407+ # value of None
408+
409+ # Some graphs has a second int parameter like HknHarary
410+ self .n : Optional [int ] = None
411+
412+ # Trees have a root
413+ self .root : Optional [int ] = None
414+
383415 def __hash__ (self ):
384416 return hash (("Pymathics`Graph" , self .G ))
385417
@@ -424,14 +456,13 @@ def coalesced_graph(self, evaluation):
424456
425457 return new_graph , "WEIGHT"
426458
427- def delete_edges (self , edges_to_delete ):
459+ def delete_edges (self , edges_to_delete ) -> "Graph" :
428460 G = self .G .copy ()
429461 directed = G .is_directed ()
430462
431- edges_to_delete = list (_normalize_edges (edges_to_delete ))
432- edges_to_delete = self .edges .filter (edges_to_delete )
463+ normalized_edges_to_delete = list (_normalize_edges (edges_to_delete ))
433464
434- for edge in edges_to_delete :
465+ for edge in normalized_edges_to_delete :
435466 if edge .has_form ("DirectedEdge" , 2 ):
436467 if directed :
437468 u , v = edge .elements
@@ -444,12 +475,7 @@ def delete_edges(self, edges_to_delete):
444475 else :
445476 G .remove_edge (u , v )
446477
447- edges = self .edges .clone ()
448- edges .delete (edges_to_delete )
449-
450- return Graph (
451- self .vertices , edges , G , self .layout , self .options , self .highlights
452- )
478+ return Graph (G )
453479
454480 def delete_vertices (self , vertices_to_delete ):
455481 G = self .G .copy ()
@@ -800,8 +826,7 @@ class AdjacencyList(_NetworkXBuiltin):
800826 :Adjacency list:
801827 https://en.wikipedia.org/wiki/Adjacency_list</url> (<url>
802828 :NetworkX:
803- https://networkx.org/documentation/networkx-2.8.8/reference/readwrite/adjlist.html</url>,
804- <url>
829+ https://networkx.org/documentation/networkx-2.8.8/reference/readwrite/adjlist.html</url>, <url>
805830 :WMA:
806831 https://reference.wolfram.com/language/ref/AdjacencyList.html</url>)
807832
@@ -1401,8 +1426,8 @@ class VertexDelete(_NetworkXBuiltin):
14011426
14021427 summary_text = "remove a vertex"
14031428
1404- def eval (self , graph , what , expression , evaluation , options ):
1405- "%(name)s [graph_, what_, OptionsPattern[%(name)s ]]"
1429+ def eval (self , graph , what , expression , evaluation , options ) -> Optional [ Graph ] :
1430+ "VertexDelete [graph_, what_, OptionsPattern[VertexDelete ]]"
14061431 graph = self ._build_graph (graph , evaluation , options , expression )
14071432 if graph :
14081433 from mathics .builtin import pattern_objects
@@ -1516,41 +1541,46 @@ class UndirectedEdge(Builtin):
15161541# return mathics_graph.add_edges(*zip(*[_parse_item(what)]))
15171542
15181543
1519- # class EdgeDelete(_NetworkXBuiltin):
1520- # """
1521- # >> Length[EdgeList[EdgeDelete[{a -> b, b -> c, c -> d}, b -> c]]]
1522- # = 2
1544+ class EdgeDelete (_NetworkXBuiltin ):
1545+ """
1546+ <url>
1547+ :WMA:
1548+ https://reference.wolfram.com/language/ref/EdgeDelete.html</url>
15231549
1524- # >> Length[EdgeList[EdgeDelete[{a -> b, b -> c, c -> b, c -> d}, b <-> c]]]
1525- # = 4
1550+ <dl>
1551+ <dt>'EdgeDelete'[$g$, $edge$]
1552+ <dd>remove the edge $edge$.
1553+ </dl>
15261554
1527- # >> Length[EdgeList[EdgeDelete[{a -> b, b < -> c, c -> d }, b -> c]] ]
1528- # = 3
1555+ >> g = Graph[{1 -> 2, 2 -> 3, 3 -> 1 }, VertexLabels->True ]
1556+ = -Graph-
15291557
1530- # >> Length[ EdgeList[EdgeDelete[{a -> b, b < -> c, c -> d}, c -> b] ]]
1531- # = 3
1558+ >> EdgeList[EdgeDelete[g, 2 -> 3 ]]
1559+ = {{1, 2}, {3, 1}}
15321560
1533- # >> Length[EdgeList[EdgeDelete[{a -> b, b <-> c, c -> d}, b <-> c]] ]
1534- # = 2
1561+ ## >> g = Graph[{4<->5,5 <->7,7<->9,9 <->5,2->4,4->6,6->2}, VertexLabels->True ]
1562+ ## = -Graph-
15351563
1536- # >> EdgeDelete[{4<->5,5<->7,7<->9,9<->5,2->4,4->6,6->2} , _UndirectedEdge]
1537- # = -Graph-
1538- # """
1564+ ## >> EdgeDelete[g , _UndirectedEdge]
1565+ ## = -Graph-
1566+ """
15391567
1540- # def eval(self, graph, what, expression, evaluation, options):
1541- # "%(name)s[graph_, what_, OptionsPattern[%(name)s]]"
1542- # graph = self._build_graph(graph, evaluation, options, expression)
1543- # if graph:
1544- # from mathics.builtin import pattern_objects
1545-
1546- # head_name = what.get_head_name()
1547- # if head_name in pattern_objects:
1548- # cases = Expression(
1549- # SymbolCases, ListExpression(*graph.edges), what
1550- # ).evaluate(evaluation)
1551- # if cases.get_head_name() == "System`List":
1552- # return graph.delete_edges(cases.elements)
1553- # elif head_name == "System`List":
1554- # return graph.delete_edges(what.elements)
1555- # else:
1556- # return graph.delete_edges([what])
1568+ summary_text = "remove an edge"
1569+
1570+ def eval (self , graph , what , expression , evaluation , options ) -> Optional [Graph ]:
1571+ "EdgeDelete[graph_, what_, OptionsPattern[EdgeDelete]]"
1572+ graph = self ._build_graph (graph , evaluation , options , expression )
1573+ if graph :
1574+ from mathics .builtin import pattern_objects
1575+
1576+ head_name = what .get_head_name ()
1577+ if head_name in pattern_objects :
1578+ cases = Expression (
1579+ SymbolCases , to_mathics_list (* graph .edges ), what
1580+ ).evaluate (evaluation )
1581+ if cases .get_head_name () == "System`List" :
1582+ return graph .delete_edges (cases .elements )
1583+ elif head_name == "System`List" :
1584+ return graph .delete_edges (what .elements )
1585+ else :
1586+ return graph .delete_edges ([what ])
0 commit comments