Networkx Enhancement Proposal
===================================

Refactor Base Graph Classes to simplify type-checking and code maintenance
----------------------------------------------------------------------------

This NEP proposes a refactoring of the Graph classes in networkx.
The need for doing so stems from desire to have a simpler weighted
graph class than XGraph and to isolate the type-checking often needed
for multiedge graphs.  Other goals are to reduce type-checking for 
edges (2 or 3 tuple), checking for selfloops, and whether data exists 
for an edge.

Specific proposal goals
-----------------------

 - add edge_data to Graph() in a way that does not change current API or code.
 - add routines to Graph() which manipulate edge_data assuming that it 
   is numerical.
 - distinguish in the API between edges (2-tuples) and edges_with_data 
   (3-tuples)
 - make XGraph a multiedge graph.
 - remove the selfloops option/restriction and make reporting of degree 
   appropriate yet still fast.
 - Retain the distinction between DiGraph and Graph.
 - Add code to each (major) algorithm or tests for each algorithm to 
   ensure that it either works for all classes, or warns if it is used on 
   an inappropriate graph type.

The resulting structure will still have 4 base Graph classes.  
The Graph class will include all single edge graphs.
The XGraph class will include all multiedge graphs.
DiGraph and XDiGraph will be directed versions of Graph and XGraph.

Edges will be able to have data with a default value of 1(instead of None).  
The methods used to add edges with or without data will be separated.  
All code which used to work for the simple Graph class must be as 
fast as it currently is.  New routines will allow the manipulating of
edge data.

The distinction between 2-tuple edges and 3-tuple edges will be made explicit
and enforced, so that there is a different routine to add 2-tuple edges than
3-tuple edges.  This will reduce the inline code checking for the
length of edge.


The flags for selfloops and multiedges will be eliminated and routines 
manipulating those properties removed where appropriate.  Selfloops will
not be excluded from add_edge and will not be included in degree calculations.
A separate routine will be available for computing degree by double counting
edges which are selfloops.

Multiedges will be confined to XGraph and XDiGraph with methods optimized for
multiedge computations.  This should allow us to 1) add new routines without
checking whether multiedges work and 2) focus on allowing multiedges as a 
separate concept for each algorithm.

Current Graph() -> Proposed Graph()
-----------------------------------
The following list compares the proposed Graph class methods with
current Graph.

Characters in the left margin indicate changes needed:: 

 ' ': no change. 
 M  : modify code but same operation.  
 A  : add new method.  
 S  : change to accomodate selfloops.


Comments after function are notes about what changes::

     __init__(self, data=None, name='')
     __str__(self)
     __iter__(self)
     __contains__(self,n)
     __len__(self)
     __getitem__(self,n)
     prepare_nbunch(self,nbunch=None)
     info(self, n=None)
     add_node(self, n)
     add_nodes_from(self, nbunch)
     delete_node(self,n)
     delete_nodes_from(self,nbunch)
     nodes_iter(self)
     nodes(self)
     number_of_nodes(self)
     has_node(self,n)
     order(self)
  S  add_edge(self, u, v=None)  
  S  add_edges_from(self, ebunch)  # ebunch is collection of 2-tuple
 AS  add_edge_with_data(self, u, v, data) #No 3-tuples--use  G.add_edge_with_graph(*e) 
 AS  add_edges_with_data_from(self, ebunch_with_data) # ebunch_with_data is collection of 3-tuples 
     delete_edge(self, u, v=None) 
     delete_edges_from(self, ebunch)   # ebunch is collection of 2-tuples
     has_edge(self, u, v=None)
     has_neighbor(self, u, v)
 M   get_edge_data(self, u, v)    # rename of get_edge
     neighbors_iter(self,n)
     neighbors(self, n)
 A   neighbors_iter_with_data(self,n)  # returns (nbr,d) tuples
 A   neighbors_with_data(self, n)      # returns {nbr:d} dict
     edges_iter(self, nbunch=None)
     edges(self, nbunch=None)
 A   edges_with_data_iter(self, nbunch=None) # returns 3-tuples
 A   edges_with_data(self, nbunch=None)      # returns list of 3-tuples
 A   edge_data_iter(self, nbunch=None)       # returns data only
 A   edge_data(self, nbunch=None)            # returns list of data only
     edge_boundary(self, nbunch1, nbunch2=None)
     node_boundary(self, nbunch1, nbunch2=None)
  S  degree(self, nbunch=None, with_labels=False)
  S  degree_iter(self,nbunch=None,with_labels=False)
 MS  weighted_degree(self, nbunch=None, with_labels=False)
 MS  weighted_degree_iter(self,nbunch=None,with_labels=False)
     clear(self)
     copy(self)
     to_undirected(self)
     to_directed(self)
 M   subgraph(self, nbunch, inplace=False, create_using=None)
     add_path(self, nlist)
     add_cycle(self, nlist)
 A   add_path_with_data(self, nlist, datalist)  # datalist is 1 shorter than nlist
 A   add_cycle_with_data(self, nlist, datalist) # datalist is 1 shorter than nlist
     is_directed(self)
     number_of_edges(self)
     size(self)
 A   weighted_size(self)
 AS  nodes_with_selfloops(self)
 AS  selfloop_edges(self)
 AS  number_of_selfloops(self)
     
 
XGraph() -> Proposed Graph()
----------------------------
Comparison of methods for proposed Graph with current XGraph without
multiedges. All code for XGraph would have to be changed to remove
multiedges, so I don't include those changes.  This is thus the
effective change for a weighted graph using XGraph under the proposed
Graph class.


::

 ' ': no change  
 M  : modify code  
 S  : modify for selfloops
 X  : delete  
 A  : add method

::

     __init__(self, data=None, name='')
     __str__(self)
     __iter__(self)
     __contains__(self,n)
     __len__(self)
     __getitem__(self,n)
     prepare_nbunch(self,nbunch=None)
     info(self, n=None)
     add_node(self, n)
     add_nodes_from(self, nbunch)
     delete_node(self,n)
     delete_nodes_from(self,nbunch)
     nodes_iter(self)
     nodes(self)
     number_of_nodes(self)
     has_node(self,n)
     order(self)
 MS  add_edge(self, u, v)          # no data allowed to be input here!
 MS  add_edges_from(self, ebunch)  # ebunch is collection of 2-tuple
 AS  add_edge_with_data(self, u, v, data)  #No 3-tuples--use G.add_edge_with_graph(*e) 
 AS  add_edges_with_data_from(self, ebunch_with_data) # ebunch_with_data is collection of 3-tuples 
     delete_edge(self, u, v=None) 
     delete_edges_from(self, ebunch)   # ebunch is collection of 2-tuples
     has_edge(self, u, v)
     has_neighbor(self, u, v)
 M   get_edge_data(self, u, v)  # was get_edge
 X   get_edge_iter(self, u, v)  # not needed without multiedges
     neighbors_iter(self,n)
     neighbors(self, n)
 A   neighbors_iter_with_data(self,n)  # returns (nbr,d) tuples
 A   neighbors_with_data(self, n)      # returns {nbr:d} dict
 M   edges_iter(self, nbunch=None)
 M   edges(self, nbunch=None)
 A   edges_with_data_iter(self, nbunch=None) # returns 3-tuples
 A   edges_with_data(self, nbunch=None)      # returns list of 3-tuples
 A   edge_data_iter(self, nbunch=None)       # returns data only
 A   edge_data(self, nbunch=None)            # returns list of data only
     edge_boundary(self, nbunch1, nbunch2=None)
     node_boundary(self, nbunch1, nbunch2=None)
  S  degree(self, nbunch=None, with_labels=False)
  S  degree_iter(self,nbunch=None,with_labels=False)
  S  weighted_degree(self, nbunch=None, with_labels=False)
  S  weighted_degree_iter(self,nbunch=None,with_labels=False)
     clear(self)
     copy(self)
     to_undirected(self)
     to_directed(self)
     subgraph(self, nbunch, inplace=False, create_using=None)
     add_path(self, nlist)
     add_cycle(self, nlist)
 A   add_path_with_data(self, nlist, datalist)  # datalist is 1 shorter than nlist
 A   add_cycle_with_data(self, nlist, datalist) # datalist is 1 shorter than nlist
     is_directed(self)
     number_of_edges(self)
     size(self)
 A   weighted_size(self)
 X   delete_multiedge(self, u, v)
  S  nodes_with_selfloops(self)
  S  selfloop_edges(self)
  S  number_of_selfloops(self)
 X   allow_selfloops(self)
 X   remove_all_selfloops(self)
 X   ban_selfloops(self)
 X   allow_multiedges(self)
 X   remove_all_multiedges(self)
 X   ban_multiedges(self)
 
 
 
Moving ahead
============

I would like feedback on this proposal.  The earlier we can identify
issues where decisions have to be made, the better off we are.  That
said, I will start coding for this as a separate branch on the
subversion repository.  The first step will be to refactor the class
modules, then utils.py and convert.py followed by generators,
non-class modules and the drawing routines.  Tests and docstrings will
be updated as we progress in addition to a periodic sweep through the
docs at milestones.


