@@ -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
756779class 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+
772817class PathGraph (_NetworkXBuiltin ):
773818 """
774819 >> PathGraph[{1, 2, 3}]
0 commit comments