;ò
×V£=c           @   su   d  Z  d f  d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e e f d „  ƒ  YZ d	 f  d
 „  ƒ  YZ d S(   sƒ  Some graph-theoretic stuff.

See also graphviz (http://www.graphviz.org/ and man pages for: twopi, dotty,
lefty, acyclic, lneato, nop, tred, gc, sccmap, unflatten, gvgpr, dot, neato,
ccomps and gvcolorize) for visualisation of graphs.  Things to play at:

  * .h file inclusion hierarchy; CPP sensitive ?
  * C function call hierarchy

$Id: graph.py,v 1.4 2002/10/08 22:06:15 eddy Exp $
s	   Partitionc           B   s)   t  Z d  Z d „  Z d „  Z d „  Z RS(   sš  Base-class for partitions.   Home for docs.

    A partition, P, of a set, S, is a set of disjoint sub-sets of S for which S
    is the union of all P's members.  I'll refer to the members of P as `parts';
    each part is a sub-set of S, each member of S is a member of exactly one
    part.  I'll describe members of S as nodes; and the members of the part
    containing a given node as peers of that node.

    The present implementation works with S as range(n) for some integer n; the
    nodes are integers 0 to n-1.  Where doc-strings in sub-classes of Partition
    describe a parameter as a node, an IndexError will be raised if its value is
    not in range(n).  Internal datastructures use the node as an index into
    appropriate arrays of length n.

    Partitions support len(); a new node may be added by calling append(len()).
    It is presumed that the caller had some prior knowledge of len()'s value;
    the argument to append() is asserted to be len() when __debug__ is true.

    Each new node will be in a new part when created: thus initialisation of the
    nodes creates a `discrete' partition in which each node is a part and each
    part has only one node in it.  [The presence of the empty set as one of the
    parts is formally allowed but invariably a nuisance: it is more practical to
    exclude empty.]  A partition is modified by uniting two of its parts: this
    is expressed as joining two nodes, one from each part.

    A partition can be thought of as answering two primitive questions,
      * are these two nodes in the same part as one another ?
      * which nodes are peers of this one ?
    along with the inevitable `give a complete description of yourself'.  The
    implementations of Find and Unite, below, answer the primitive questions and
    suffice, together, to provide a complete description.  Each is optimised to
    answer one question and avoids answering the other: each presumes that the
    other will be consulted for the information it lacks.  Thus Unite presumes
    that you won't ask it to join two nodes unless you know they are in
    different parts: it thereby saves itself the expensive (for it) job of
    checking this. c         C   s
   t  ‚ d S(   sO   Returns a list of the nodes in the same connected component as the given node. N(   s   NotImplementedError(   s   selfs   node(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   peers3   s     c         C   s
   t  ‚ d S(   s3   Joins the connected components of the given nodes. N(   s   NotImplementedError(   s   selfs   nodes   vertex(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   join7   s     c         G   s
   t  ‚ d S(   sE   Returns true iff all the given nodes are in one connected component. N(   s   NotImplementedError(   s   selfs   nodes   nodes(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joined;   s     (   s   __name__s
   __module__s   __doc__s   peerss   joins   joined(    (    (    s'   /home/eddy/.sys/py/study/maths/graph.pys	   Partition   s   $ 		s   Unitec           B   s>   t  Z d  Z d d „ Z d „  Z d „  Z d „  Z d „  Z RS(   s´   Keeps track of the parts of a partition.

    Methods:
      peers(node) -- returns a list of peers of the given node.
      join(this, that) -- combines two *disjoint* parts.
    i    c         C   s"   t  | ƒ |  _ t  | ƒ |  _ d S(   s#   Initialise internal datastructures.N(   s   ranges   sizes   selfs   _Unite__forwards   _Unite__backward(   s   selfs   size(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __init__G   s     c         C   s   t  |  i ƒ Sd  S(   N(   s   lens   selfs   _Unite__forward(   s   self(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __len__Q   s    c         C   sy   |  i | g  f \ } } } xW n oO | i | ƒ | | } |  i | | d j p t ‚ | | j o | Sq q% Wd S(   sè   Returns the component containing a given node.

        Result is a list of nodes in the given node's component.
        If it ever involves duplicates, something's gone wrong.
        Probably so wrong you won't see the answer ... i   iÿÿÿÿN(	   s   selfs   _Unite__forwards   nodes   fs   ns   rows   appends   _Unite__backwards   AssertionError(   s   selfs   nodes   ns   rows   f(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   peersT   s      
 c         C   s^   t  |  i ƒ | j o t  |  i ƒ j n p
 t d ‚ |  i i | ƒ |  i i | ƒ d  S(   Ns'   Unite's append requires the right value(   s   lens   selfs   _Unite__forwards   inds   _Unite__backwards   AssertionErrors   append(   s   selfs   ind(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   appendc   s    :c         C   s“   |  i |  i f \ } } | | | | f \ } } | | | j o | | | j p t	 ‚ | | f \ | | <| | <| | f \ | | <| | <d S(   sÆ   Joins two *disjoint* components.

        Arguments are nodes, one from each component.

        The nodes must not be peers: this is *not* checked.
        They will be peers after join is called. N(
   s   selfs   _Unite__forwards   _Unite__backwards   fs   bs   nods   vers   pods   wers   AssertionError(   s   selfs   nods   vers   bs   fs   wers   pod(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joink   s     )(   s   __name__s
   __module__s   __doc__s   __init__s   __len__s   peerss   appends   join(    (    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   Unite?   s    
			s   Findc           B   sY   t  Z d  Z d d „ Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z	 d	 „  Z
 RS(
   sÌ  Keeps track of `in same part' truths for a partition.

    Handles predicates relating to a partition; also keeps track of the
    collection of parts, expressed as one member of each part.

    Methods:
      joined(node, ...) -- true if all given nodes are in one part
      join(this, that) -- ensures two nodes are joined
      disjoint() -- returns a list of nodes, one from each part
      peercount(node) -- size of given node's connected component
    i    c         C   s#   t  | ƒ |  _ d g | |  _ d  S(   Ni   (   s   ranges   sizes   selfs	   _Find__ups   _Find__count(   s   selfs   size(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __init__‡   s    c         C   s   t  |  i ƒ Sd  S(   N(   s   lens   selfs	   _Find__up(   s   self(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __len__   s    c         C   sq   g  } x> n o6 |  i | } | | j o Pn | i | ƒ | } q Wx | d  D] } | |  i | <qR W| Sd S(   se  Returns the representative member of node's connected component.

        Single argument is a node (index): returns the node reached from this by
        chasing i->self.__up[i] to a fixed point.  Side-effect: changes the
        __up[] of all nodes it visits on the way to point at the answer
        returned, so as to speed this chase next time around. i   iÿÿÿÿN(   s   trails   selfs	   _Find__ups   nodes   ns   append(   s   selfs   nodes   trails   n(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __chase’   s         c         C   s   |  i |  i | ƒ Sd  S(   N(   s   selfs   _Find__counts   _Find__chases   node(   s   selfs   node(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys	   peercount§   s    c         G   sL   |  i | ƒ } x, | D]$ } |  i | ƒ | j o t Sq q Wd d j Sd S(   s  Do the edges seen thus far connect (all) the given nodes ?

        Arguments are node indices; at least one must be given.  Result is true
        precisely if all nodes are in the same connected component of the graph
        of edges thus far passed to self.join(). i   i    N(   s   selfs   _Find__chases   nodes   nods   nodess   vertexs   None(   s   selfs   nodes   nodess   nods   vertex(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joined©   s      c         C   s^   t  |  i ƒ | j o t  |  i ƒ j n p
 t d ‚ |  i i | ƒ |  i i d ƒ d  S(   Ns&   Find's append requires the right valuei   (   s   lens   selfs   _Find__counts   inds	   _Find__ups   AssertionErrors   append(   s   selfs   ind(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   append·   s    :c         C   s®   |  i | ƒ |  i | ƒ f \ } } | | j o d Sn |  i } | | | | f \ } } | | j  o | | f \ } } n | | | f \ |  i
 | <| | <d d j Sd S(   s¼   Joins two nodes.

        Returns None if nodes were already joined: else, a true value.
        Consequently its return is what `not self.joined()' would have yielded
        previously. Ni   i    (   s   selfs   _Find__chases   nodes   vertexs   nods   vers   _Find__counts   counts   ns   vs	   _Find__up(   s   selfs   nodes   vertexs   counts   vers   nods   ns   v(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joinÀ   s     $ 	 !c         C   s)   t  |  i d „ t t |  i ƒ ƒ ƒ Sd S(   sK   Returns a list containing one sample member from each connected component. c         C   s   |  | |  j S(   N(   s   xs   _u(   s   xs   _u(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   <lambda>Ø   s    N(   s   filters   selfs	   _Find__ups   ranges   len(   s   self(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   disjointÔ   s     (   s   __name__s
   __module__s   __doc__s   __init__s   __len__s   _Find__chases	   peercounts   joineds   appends   joins   disjoint(    (    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   Findz   s    							s	   FindUnitec           B   s5   t  Z d  Z d d „ Z d „  Z d „  Z d „  Z RS(   s`  An implementation of the find-unite algorithm.

    Provides for arbitrary graph-partitioning, provided the nodes of the graph
    are labelled with an initial sub-sequence of the natural numbers, i.e. 0, 1,
    ..., n for some natural n, with no omissions.  Graph, below, provides such a
    packaging for a graph with arbitrary python objects as nodes.

    Create an instance of FindUnite(); for each edge in a graph, identify the
    two ends and invoke the instance's .join(thisend, thatend).  At any stage,
    invoke .peers(node) to get a list of all nodes in the given one's connected
    component. i    c         C   s$   t  i |  | ƒ t i |  | ƒ d S(   s   Initialises an empty FindUnite.N(   s   Finds   __init__s   selfs   sizes   Unite(   s   selfs   size(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __init__ç   s     c         C   s$   t  i |  | ƒ t i |  | ƒ d  S(   N(   s   Finds   appends   selfs   inds   Unite(   s   selfs   ind(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   appendí   s    c         C   s1   t  i |  | | ƒ o t i |  | | ƒ n d S(   s0  Joins a given pair of nodes.

        Takes two arguments: the end-nodes of an edge in your graph.  Joins
        their connected components, if disjoint, together.  Nodes not previously
        known to self are added, initially connected to nothing; they are then
        joined as for familiar nodes. N(   s   Finds   joins   selfs   nodes   vertexs   Unite(   s   selfs   nodes   vertex(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joinñ   s     c         C   s   t  |  i |  i ƒ  ƒ Sd S(   sÒ   Returns a list of disjoint lists describing the partition.

        Each entry in the list returned is a list of nodes representing a
        connected component of the graph described by the FindUnite object. N(   s   maps   selfs   peerss   disjoint(   s   self(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys	   partitionÿ   s     (   s   __name__s
   __module__s   __doc__s   __init__s   appends   joins	   partition(    (    (    s'   /home/eddy/.sys/py/study/maths/graph.pys	   FindUniteÚ   s
    		s   Graphc           B   sh   t  Z d  Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z	 d „  Z
 d	 „  Z d
 „  Z RS(   sÚ  Represents a network of nodes joined by edges.

    Creation:

      Graph([node, ...]) -- creates a new graph with, optionally, the given
                            objects as (initially) unconnected nodes.

    Command:

      join(this, that) -- adds an edge from this to that, optionally adding each
                          as a node in the process.

    None of the following modifies the graph: whereas join() will add any
    unfamiliar arguments as nodes of the graph, the queries (below) will
    interprete any unrecognised node as being outside the graph and connected to
    nothing but itself - but will promptly forget they ever heard of the given
    node.

    Attributes:

      nodes -- a tuple containing all known nodes.

      edges -- a tuple containing all known edges, each of which is represented
               by a 2-tuple of nodes.

      partition -- a list of connected components of self.  Each connected
                   component is represented as a list (whose order is arbitrary)
                   of nodes; each such list is equal, subject to shuffling
                   order, to the .peers(n) of each node, n, in the list.  Each
                   node of the graph appears in exactly one of the connected
                   components.

    Queries:

      peers(node) -- returns a list of all nodes in the same connected component
                     as the given node (order is arbitrary).

      peercount(node) -- returns the value len(peers(node)) would produce: the
                         number of nodes in the connected component in which the
                         given node appears.

      joined(node, ...) -- tests whether given nodes are all in one connected
                           component.

      span(node, ...) -- returns a Graph consisting of all nodes and arcs in the
                         same connected component as at least one of the given
                         nodes.

      chop(node, ...) -- returns a Graph consisting of the given nodes and any
                         edges directly connecting these nodes.

    Note that .span() provides a sub-graph `grown outwards from' its given
    nodes, while .chop() provides a sub-graph `stripped down to only' the given
    nodes. c         G   s7   g  t  | ƒ t t | ƒ ƒ f \ |  _ |  _ |  _ d  S(   N(   s   lists   nodess	   FindUnites   lens   selfs   _Graph__edgess   _Graph__nodess   _Graph__connect(   s   selfs   nodes(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __init__@  s    c         C   ss   | d j o t |  i ƒ Sn | d j o t |  i ƒ Sn | d j o t |  i |  i i ƒ  ƒ Sn t	 | ‚ d  S(   Ns   nodess   edgess	   partition(
   s   keys   tuples   selfs   _Graph__nodess   _Graph__edgess   maps   _Graph__peerss   _Graph__connects   disjoints   AttributeError(   s   selfs   key(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __getattr__D  s       c         C   sa   y |  i i | ƒ SWn t j
 o n Xt |  i ƒ } |  i i | ƒ |  i i | ƒ | Sd S(   s/  Returns internal index of node, adding node to graph if needed.

        If node has been met before, an index has been recorded for it: this is
        returned.  Otherwise, the node is added to internal datastructures with
        a previously-unused index, which is returned.  For internal use only. N(	   s   selfs   _Graph__nodess   indexs   nodes
   ValueErrors   lens   inds   appends   _Graph__connect(   s   selfs   nodes   ind(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __nodeN  s       c         C   s?   |  i i | | f ƒ |  i i |  i | ƒ |  i | ƒ ƒ d S(   sH   Connects two nodes in the present graph, adding the nodes if necessary. N(   s   selfs   _Graph__edgess   appends   starts   stops   _Graph__connects   joins   _Graph__node(   s   selfs   starts   stop(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   join_  s     c         G   sˆ   t  | ƒ d j  o t d ƒ ‚ n t  | ƒ d j  o d Sn y t |  i i | ƒ } Wn t j
 o t Sn Xt	 |  i
 i | ƒ Sd  S(   Ni   s<   no nodes provided: how can I check whether they are joined ?i   (   s   lens   nodess
   ValueErrors   maps   selfs   _Graph__nodess   indexs   indicess   Nones   applys   _Graph__connects   joined(   s   selfs   nodess   indices(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   joinedg  s       	c         C   sD   y |  i i | ƒ } Wn t j
 o d Sn X|  i i | ƒ Sd  S(   Ni   (   s   selfs   _Graph__nodess   indexs   nodes   nods
   ValueErrors   _Graph__connects	   peercount(   s   selfs   nodes   nod(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys	   peercountq  s
      	c         C   s&   t  |  i d „ |  i i | ƒ ƒ Sd  S(   Nc         C   s   | |  S(   N(   s   _rs   i(   s   is   _r(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   <lambda>x  s    (   s   maps   selfs   _Graph__nodess   _Graph__connects   peerss   nod(   s   selfs   nod(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   __peersw  s    c         C   sD   y |  i i | ƒ } Wn t j
 o | g Sn X|  i | ƒ Sd  S(   N(   s   selfs   _Graph__nodess   indexs   nodes   nods
   ValueErrors   _Graph__peers(   s   selfs   nodes   nod(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   peersz  s
      c         G   s¶   g  } x2 | D]* } | | j o | |  i | ƒ } q q Wt t t | ƒ ƒ } x[ |  i	 D]P \ } } | | j | | j j p
 t d ‚ | | j o | i | | ƒ qZ qZ W| Sd S(   s5   Returns a full sub-graph containing the given nodes. s#   I thought we had a partition here !N(   s   alls   nodess   nodes   selfs   peerss   applys   Graphs   tuples   anss   _Graph__edgess   as   bs   AssertionErrors   join(   s   selfs   nodess   nodes   as   alls   bs   ans(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   span‚  s      
 # c         G   s_   t  t | ƒ } xE |  i D]: \ } } | | j o
 | | j o | i | | ƒ q q W| Sd S(   sï   Returns a restriction sub-graph containing only the given nodes.

        Includes each edge of self whose ends are both in the restriction.  In
        particular, doesn't include linkage via nodes omitted (see .span() for
        that). N(	   s   applys   Graphs   nodess   anss   selfs   _Graph__edgess   as   bs   join(   s   selfs   nodess   as   bs   ans(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   chop”  s     
  (   s   __name__s
   __module__s   __doc__s   __init__s   __getattr__s   _Graph__nodes   joins   joineds	   peercounts   _Graph__peerss   peerss   spans   chop(    (    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   Graph  s   6 		
			
				N(   s   __doc__s	   Partitions   Unites   Finds	   FindUnites   Graph(   s   Graphs	   Partitions   Finds   Unites	   FindUnite(    (    s'   /home/eddy/.sys/py/study/maths/graph.pys   ?   s   2;`-¬