Skip to content

Commit 56b18ff

Browse files
committed
Add tree checking. Add 2 expr Graph and TreeGraph
1 parent f474239 commit 56b18ff

File tree

1 file changed

+66
-21
lines changed

1 file changed

+66
-21
lines changed

pymathics/graph/__main__.py

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -490,20 +490,42 @@ def _parse_item(x, attr_dict=None):
490490
return x, attr_dict
491491

492492

493-
def _graph_from_list(rules, options):
493+
def _graph_from_list(rules, options, new_vertices=None):
494494
if not rules:
495495
return Graph(nx.Graph())
496496
else:
497497
new_edges, new_edge_properties = zip(*[_parse_item(x) for x in rules])
498-
return _create_graph(new_edges, new_edge_properties, options)
498+
return _create_graph(new_edges, new_edge_properties,
499+
options=options,
500+
new_vertices=new_vertices)
499501

500502

501-
def _create_graph(new_edges, new_edge_properties, options, from_graph=None):
503+
def _create_graph(new_edges, new_edge_properties, options, from_graph=None, new_vertices=None):
504+
505+
known_vertices = set()
506+
vertices = []
507+
vertex_properties = []
508+
509+
def add_vertex(x, attr_dict=None):
510+
if x.has_form("Property", 2):
511+
expr, prop = x.leaves
512+
attr_dict = _parse_property(prop, attr_dict)
513+
return add_vertex(expr, attr_dict)
514+
elif x not in known_vertices:
515+
known_vertices.add(x)
516+
vertices.append(x)
517+
vertex_properties.append(attr_dict)
518+
return x
519+
502520
directed_edges = []
503521
undirected_edges = []
504522

523+
if new_vertices is not None:
524+
vertices = [add_vertex(v) for v in new_vertices]
525+
505526
if from_graph is not None:
506-
vertices, vertex_properties = from_graph.vertices.data()
527+
old_vertices, vertex_properties = from_graph.vertices.data()
528+
vertices += old_vertices
507529
edges, edge_properties = from_graph.edges.data()
508530

509531
for edge, attr_dict in zip(edges, edge_properties):
@@ -515,26 +537,15 @@ def _create_graph(new_edges, new_edge_properties, options, from_graph=None):
515537

516538
multigraph = [from_graph.is_multigraph()]
517539
else:
518-
vertices = []
519-
vertex_properties = []
520540
edges = []
521541
edge_properties = []
522542

523543
multigraph = [False]
524544

525-
known_vertices = set(vertices)
526545
known_edges = set(edges)
527-
528-
def add_vertex(x, attr_dict=None):
529-
if x.has_form("Property", 2):
530-
expr, prop = x.leaves
531-
attr_dict = _parse_property(prop, attr_dict)
532-
return add_vertex(expr, attr_dict)
533-
elif x not in known_vertices:
534-
known_vertices.add(x)
535-
vertices.append(x)
536-
vertex_properties.append(attr_dict)
537-
return x
546+
# It is simpler to just recompute this than change the above to work
547+
# incrementally
548+
known_vertices = set(vertices)
538549

539550
def track_edges(*edges):
540551
if multigraph[0]:
@@ -631,9 +642,17 @@ def full_new_edge_properties(new_edge_style):
631642
empty_dict = {}
632643
if directed_edges:
633644
G = nx.MultiDiGraph() if multigraph[0] else nx.DiGraph()
645+
nodes_seen = set()
634646
for u, v, attr_dict in directed_edges:
635647
attr_dict = attr_dict or empty_dict
636648
G.add_edge(u, v, **attr_dict)
649+
nodes_seen.add(u)
650+
nodes_seen.add(v)
651+
652+
unseen_vertices = set(vertices) - nodes_seen
653+
for v in unseen_vertices:
654+
G.add_node(v)
655+
637656
for u, v, attr_dict in undirected_edges:
638657
attr_dict = attr_dict or empty_dict
639658
G.add_edge(u, v, **attr_dict)
@@ -752,6 +771,10 @@ def apply(self, graph, evaluation, options):
752771
"Graph[graph_List, OptionsPattern[%(name)s]]"
753772
return _graph_from_list(graph.leaves, options)
754773

774+
def apply_1(self, vertices, edges, evaluation, options):
775+
"Graph[vertices_List, edges_List, OptionsPattern[%(name)s]]"
776+
return _graph_from_list(edges.leaves, options=options, new_vertices=vertices.leaves)
777+
755778

756779
class TreeGraphAtom(AtomBuiltin):
757780
"""
@@ -762,13 +785,35 @@ class TreeGraphAtom(AtomBuiltin):
762785

763786
options = DEFAULT_TREE_OPTIONS
764787

765-
def apply(self, graph, evaluation, options):
766-
"TreeGraph[graph_List, OptionsPattern[%(name)s]]"
767-
g = _graph_from_list(graph.leaves, options)
788+
messages = {
789+
"v": "Expected first parameter vertices to be a list of vertices",
790+
"notree": "Graph is not a tree.",
791+
}
792+
793+
def apply(self, rules, evaluation, options):
794+
"TreeGraph[rules_List, OptionsPattern[%(name)s]]"
795+
g = _graph_from_list(rules.leaves, options)
796+
if not nx.is_tree(g.G):
797+
evaluation.message(self.get_name(), "notree")
798+
799+
g.G.graph_layout = String("tree")
800+
# Compute/check/set for root?
801+
return g
802+
803+
def apply_1(self, vertices, edges, evaluation, options):
804+
"TreeGraph[vertices_List, edges_List, OptionsPattern[%(name)s]]"
805+
if not all(isinstance(v, Atom) for v in vertices.leaves):
806+
evaluation.message(self.get_name(), "v")
807+
808+
g = _graph_from_list(edges.leaves, options=options, new_vertices=vertices.leaves)
809+
if not nx.is_tree(g.G):
810+
evaluation.message(self.get_name(), "notree")
811+
768812
g.G.graph_layout = String("tree")
769813
# Compute/check/set for root?
770814
return g
771815

816+
772817
class PathGraph(_NetworkXBuiltin):
773818
"""
774819
>> PathGraph[{1, 2, 3}]

0 commit comments

Comments
 (0)