;ò
×V£=c@sudZdfd„ƒYZdefd„ƒYZdefd„ƒYZdeefd„ƒYZd fd
„ƒYZdS(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 PartitioncBs)tZdZd„Zd„Zd„ZRS(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. cCs
t‚dS(sOReturns a list of the nodes in the same connected component as the given node. N(sNotImplementedError(sselfsnode((s'/home/eddy/.sys/py/study/maths/graph.pyspeers3scCs
t‚dS(s3Joins the connected components of the given nodes. N(sNotImplementedError(sselfsnodesvertex((s'/home/eddy/.sys/py/study/maths/graph.pysjoin7scGs
t‚dS(sEReturns true iff all the given nodes are in one connected component. N(sNotImplementedError(sselfsnodesnodes((s'/home/eddy/.sys/py/study/maths/graph.pysjoined;s(s__name__s
__module__s__doc__speerssjoinsjoined(((s'/home/eddy/.sys/py/study/maths/graph.pys Partition
s$ sUnitecBs>tZdZdd„Zd„Zd„Zd„Zd„ZRS(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.
icCs"t|ƒ|_t|ƒ|_dS(s#Initialise internal datastructures.N(srangessizesselfs_Unite__forwards_Unite__backward(sselfssize((s'/home/eddy/.sys/py/study/maths/graph.pys__init__GscCst|iƒSdS(N(slensselfs_Unite__forward(sself((s'/home/eddy/.sys/py/study/maths/graph.pys__len__QscCsy|i|gf\}}}xWnoO|i|ƒ||}|i||djpt‚||jo|Sqq%WdS(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 ... iiÿÿÿÿN( sselfs_Unite__forwardsnodesfsnsrowsappends_Unite__backwardsAssertionError(sselfsnodesnsrowsf((s'/home/eddy/.sys/py/study/maths/graph.pyspeersTs
cCs^t|iƒ|jot|iƒjnp
td‚|ii|ƒ|ii|ƒdS(Ns'Unite's append requires the right value(slensselfs_Unite__forwardsinds_Unite__backwardsAssertionErrorsappend(sselfsind((s'/home/eddy/.sys/py/study/maths/graph.pysappendcs:cCs“|i|if\}}||||f\}}|||jo|||jpt ‚||f\||<||<||f\||<||no6|i|}||joPn|i|ƒ|}qWx|d D]}||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. iiÿÿÿÿN(strailsselfs _Find__upsnodesnsappend(sselfsnodestrailsn((s'/home/eddy/.sys/py/study/maths/graph.pys__chase’s
cCs|i|i|ƒSdS(N(sselfs_Find__counts_Find__chasesnode(sselfsnode((s'/home/eddy/.sys/py/study/maths/graph.pys peercount§scGsL|i|ƒ}x,|D]$}|i|ƒ|jotSqqWddjSdS(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(). iiN(sselfs_Find__chasesnodesnodsnodessvertexsNone(sselfsnodesnodessnodsvertex((s'/home/eddy/.sys/py/study/maths/graph.pysjoined©scCs^t|iƒ|jot|iƒjnp
td‚|ii|ƒ|iidƒdS(Ns&Find's append requires the right valuei(slensselfs_Find__countsinds _Find__upsAssertionErrorsappend(sselfsind((s'/home/eddy/.sys/py/study/maths/graph.pysappend·s:cCs®|i|ƒ|i|ƒf\}}||jodSn|i}||||f\}}||jo||f\}}n|||f\|i
|<||ØsN(sfiltersselfs _Find__upsrangeslen(sself((s'/home/eddy/.sys/py/study/maths/graph.pysdisjointÔs(s__name__s
__module__s__doc__s__init__s__len__s_Find__chases peercountsjoinedsappendsjoinsdisjoint(((s'/home/eddy/.sys/py/study/maths/graph.pysFindzs s FindUnitecBs5tZdZdd„Zd„Zd„Zd„ZRS(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. icCs$ti||ƒti||ƒdS(sInitialises an empty FindUnite.N(sFinds__init__sselfssizesUnite(sselfssize((s'/home/eddy/.sys/py/study/maths/graph.pys__init__çscCs$ti||ƒti||ƒdS(N(sFindsappendsselfsindsUnite(sselfsind((s'/home/eddy/.sys/py/study/maths/graph.pysappendíscCs1ti|||ƒoti|||ƒndS(s0Joins 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(sFindsjoinsselfsnodesvertexsUnite(sselfsnodesvertex((s'/home/eddy/.sys/py/study/maths/graph.pysjoinñscCst|i|iƒƒSdS(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(smapsselfspeerssdisjoint(sself((s'/home/eddy/.sys/py/study/maths/graph.pys partitionÿs(s__name__s
__module__s__doc__s__init__sappendsjoins partition(((s'/home/eddy/.sys/py/study/maths/graph.pys FindUniteÚs
sGraphcBshtZdZd„Zd„Zd„Zd„Zd„Zd„Zd„Z d„Z
d „Zd
„ZRS(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. cGs7gt|ƒtt|ƒƒf\|_|_|_dS(N(slistsnodess FindUniteslensselfs
_Graph__edgess
_Graph__nodess_Graph__connect(sselfsnodes((s'/home/eddy/.sys/py/study/maths/graph.pys__init__@scCss|djot|iƒSn|djot|iƒSn|djot|i|iiƒƒSnt |‚dS(Nsnodessedgess partition(
skeystuplesselfs
_Graph__nodess
_Graph__edgessmaps
_Graph__peerss_Graph__connectsdisjointsAttributeError(sselfskey((s'/home/eddy/.sys/py/study/maths/graph.pys__getattr__Ds
cCsay|ii|ƒSWntj
onXt|iƒ}|ii|ƒ|ii|ƒ|SdS(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( sselfs
_Graph__nodessindexsnodes
ValueErrorslensindsappends_Graph__connect(sselfsnodesind((s'/home/eddy/.sys/py/study/maths/graph.pys__nodeNscCs?|ii||fƒ|ii|i|ƒ|i|ƒƒdS(sHConnects two nodes in the present graph, adding the nodes if necessary. N(sselfs
_Graph__edgessappendsstartsstops_Graph__connectsjoins_Graph__node(sselfsstartsstop((s'/home/eddy/.sys/py/study/maths/graph.pysjoin_scGsˆt|ƒdjotdƒ‚nt|ƒdjodSnyt|ii|ƒ}Wntj
otSnXt |i
i|ƒSdS(Nis<no nodes provided: how can I check whether they are joined ?i(slensnodess
ValueErrorsmapsselfs
_Graph__nodessindexsindicessNonesapplys_Graph__connectsjoined(sselfsnodessindices((s'/home/eddy/.sys/py/study/maths/graph.pysjoinedgs cCsDy|ii|ƒ}Wntj
odSnX|ii|ƒSdS(Ni(sselfs
_Graph__nodessindexsnodesnods
ValueErrors_Graph__connects peercount(sselfsnodesnod((s'/home/eddy/.sys/py/study/maths/graph.pys peercountqs
cCs&t|id„|ii|ƒƒSdS(NcCs||S(N(s_rsi(sis_r((s'/home/eddy/.sys/py/study/maths/graph.pysxs(smapsselfs
_Graph__nodess_Graph__connectspeerssnod(sselfsnod((s'/home/eddy/.sys/py/study/maths/graph.pys__peerswscCsDy|ii|ƒ}Wntj
o|gSnX|i|ƒSdS(N(sselfs
_Graph__nodessindexsnodesnods
ValueErrors
_Graph__peers(sselfsnodesnod((s'/home/eddy/.sys/py/study/maths/graph.pyspeerszs
cGs¶g}x2|D]*}||jo||i|ƒ}q
q
Wttt|ƒƒ}x[|i D]P\}}||j||jjp
td‚||jo|i
||ƒqZqZW|SdS(s5Returns a full sub-graph containing the given nodes. s#I thought we had a partition here !N(sallsnodessnodesselfspeerssapplysGraphstuplesanss
_Graph__edgessasbsAssertionErrorsjoin(sselfsnodessnodesasallsbsans((s'/home/eddy/.sys/py/study/maths/graph.pysspan‚s
#
cGs_tt|ƒ}xE|iD]:\}}||jo
||jo|i||ƒqqW|SdS(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( sapplysGraphsnodessanssselfs
_Graph__edgessasbsjoin(sselfsnodessasbsans((s'/home/eddy/.sys/py/study/maths/graph.pyschop”s
(
s__name__s
__module__s__doc__s__init__s__getattr__s_Graph__nodesjoinsjoineds peercounts
_Graph__peersspeerssspanschop(((s'/home/eddy/.sys/py/study/maths/graph.pysGraphs6
N(s__doc__s PartitionsUnitesFinds FindUnitesGraph(sGraphs PartitionsFindsUnites FindUnite((s'/home/eddy/.sys/py/study/maths/graph.pys?s2;`-¬