#--
# Copyright (c) 2006 Shawn Patrick Garbett
# Copyright (c) 2002,2004,2005 by Horst Duchene
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice(s),
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the Shawn Garbett nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#++
require 'puppet/external/gratr/edge'
require 'puppet/external/gratr/labels'
require 'puppet/external/gratr/graph_api'
module GRATR
# Using the functions required by the GraphAPI, it implements all the
# basic functions of a Graph class by using only functions in GraphAPI.
# An actual implementation still needs to be done, as in Digraph or
# UndirectedGraph.
module Graph
include Enumerable
include Labels
include GraphAPI
# Non destructive version of add_vertex!, returns modified copy of Graph
def add_vertex(v, l=nil) x=self.class.new(self); x.add_vertex!(v,l); end
# Non destructive version add_edge!, returns modified copy of Graph
def add_edge(u, v=nil, l=nil) x=self.class.new(self); x.add_edge!(u,v,l); end
# Non destructive version of remove_vertex!, returns modified copy of Graph
def remove_vertex(v) x=self.class.new(self); x.remove_vertex!(v); end
# Non destructive version of remove_edge!, returns modified copy of Graph
def remove_edge(u,v=nil) x=self.class.new(self); x.remove_edge!(u,v); end
# Return Array of adjacent portions of the Graph
# x can either be a vertex an edge.
# options specifies parameters about the adjacency search
# :type can be either :edges or :vertices (default).
# :direction can be :in, :out(default) or :all.
#
# Note: It is probably more efficently done in implementation.
def adjacent(x, options={})
d = directed? ? (options[:direction] || :out) : :all
# Discharge the easy ones first
return [x.source] if x.kind_of? Edge and options[:type] == :vertices and d == :in
return [x.target] if x.kind_of? Edge and options[:type] == :vertices and d == :out
return [x.source, x.target] if x.kind_of? Edge and options[:type] != :edges and d == :all
(options[:type] == :edges ? edges : to_a).select {|u| adjacent?(x,u,d)}
end
# Add all objects in _a_ to the vertex set.
def add_vertices!(*a) a.each {|v| add_vertex! v}; self; end
# See add_vertices!
def add_vertices(*a) x=self.class.new(self); x.add_vertices(*a); self; end
# Add all edges in the _edges_ Enumerable to the edge set. Elements of the
# Enumerable can be both two-element arrays or instances of DirectedEdge or
# UnDirectedEdge.
def add_edges!(*args) args.each { |edge| add_edge!(edge) }; self; end
# See add_edge!
def add_edges(*a) x=self.class.new(self); x.add_edges!(*a); self; end
# Remove all vertices specified by the Enumerable a from the graph by
# calling remove_vertex!.
def remove_vertices!(*a) a.each { |v| remove_vertex! v }; end
# See remove_vertices!
def remove_vertices(*a) x=self.class.new(self); x.remove_vertices(*a); end
# Remove all vertices edges by the Enumerable a from the graph by
# calling remove_edge!
def remove_edges!(*a) a.each { |e| remove_edges! e }; end
# See remove_edges
def remove_edges(*a) x=self.class.new(self); x.remove_edges(*a); end
# Execute given block for each vertex, provides for methods in Enumerable
def each(&block) vertices.each(&block); end
# Returns true if _v_ is a vertex of the graph.
# This is a default implementation that is of O(n) average complexity.
# If a subclass uses a hash to store vertices, then this can be
# made into an O(1) average complexity operation.
def vertex?(v) vertices.include?(v); end
# Returns true if u or (u,v) is an Edge of the graph.
def edge?(*arg) edges.include?(edge_convert(*args)); end
# Tests two objects to see if they are adjacent.
# direction parameter specifies direction of adjacency, :in, :out, or :all(default)
# All denotes that if there is any adjacency, then it will return true.
# Note that the default is different than adjacent where one is primarily concerned with finding
# all adjacent objects in a graph to a given object. Here the concern is primarily on seeing
# if two objects touch. For vertexes, any edge between the two will usually do, but the direction
# can be specified if need be.
def adjacent?(source, target, direction=:all)
if source.kind_of? GRATR::Edge
raise NoEdgeError unless edge? source
if target.kind_of? GRATR::Edge
raise NoEdgeError unless edge? target
(direction != :out and source.source == target.target) or (direction != :in and source.target == target.source)
else
raise NoVertexError unless vertex? target
(direction != :out and source.source == target) or (direction != :in and source.target == target)
end
else
raise NoVertexError unless vertex? source
if target.kind_of? GRATR::Edge
raise NoEdgeError unless edge? target
(direction != :out and source == target.target) or (direction != :in and source == target.source)
else
raise NoVertexError unless vertex? target
(direction != :out and edge?(target,source)) or (direction != :in and edge?(source,target))
end
end
end
# Returns true if the graph has no vertex, i.e. num_vertices == 0.
def empty?() vertices.size.zero?; end
# Returns true if the given object is a vertex or Edge in the Graph.
#
def include?(x) x.kind_of?(GRATR::Edge) ? edge?(x) : vertex?(x); end
# Returns the neighboorhood of the given vertex (or Edge)
# This is equivalent to adjacent, but bases type on the type of object.
# direction can be :all, :in, or :out
def neighborhood(x, direction = :all)
adjacent(x, :direction => direction, :type => ((x.kind_of? GRATR::Edge) ? :edges : :vertices ))
end
# Union of all neighborhoods of vertices (or edges) in the Enumerable x minus the contents of x
# Definition taken from Jorgen Bang-Jensen, Gregory Gutin, _Digraphs: Theory, Algorithms and Applications_, pg 4
def set_neighborhood(x, direction = :all)
x.inject(Set.new) {|a,v| a.merge(neighborhood(v,direction))}.reject {|v2| x.include?(v2)}
end
# Union of all set_neighborhoods reachable in p edges
# Definition taken from Jorgen Bang-Jensen, Gregory Gutin, _Digraphs: Theory, Algorithms and Applications_, pg 46
def closed_pth_neighborhood(w,p,direction=:all)
if p <= 0
w
elsif p == 1
(w + set_neighborhood(w,direction)).uniq
else
n = set_neighborhood(w, direction)
(w + n + closed_pth_neighborhood(n,p-1,direction)).uniq
end
end
# Returns the neighboorhoods reachable in p steps from every vertex (or edge)
# in the Enumerable x
# Definition taken from Jorgen Bang-Jensen, Gregory Gutin, _Digraphs: Theory, Algorithms and Applications_, pg 46
def open_pth_neighborhood(x, p, direction=:all)
if p <= 0
x
elsif p == 1
set_neighborhood(x,direction)
else
set_neighborhood(open_pth_neighborhood(x, p-1, direction),direction) - closed_pth_neighborhood(x,p-1,direction)
end
end
# Returns the number of out-edges (for directed graphs) or the number of
# incident edges (for undirected graphs) of vertex _v_.
def out_degree(v) adjacent(v, :direction => :out).size; end
# Returns the number of in-edges (for directed graphs) or the number of
# incident edges (for undirected graphs) of vertex _v_.
def in_degree(v) adjacent(v, :direction => :in ).size; end
# Returns the sum of the number in and out edges for a vertex
def degree(v) in_degree(v) + out_degree(v); end
# Minimum in-degree
def min_in_degree() to_a.map {|v| in_degree(v)}.min; end
# Minimum out-degree
def min_out_degree() to_a.map {|v| out_degree(v)}.min; end
# Minimum degree of all vertexes
def min_degree() [min_in_degree, min_out_degree].min; end
# Maximum in-degree
def max_in_degree() vertices.map {|v| in_degree(v)}.max; end
# Maximum out-degree
def max_out_degree() vertices.map {|v| out_degree(v)}.max; end
# Minimum degree of all vertexes
def max_degree() [max_in_degree, max_out_degree].max; end
# Regular
def regular?() min_degree == max_degree; end
# Returns the number of vertices.
def size() vertices.size; end
# Synonym for size.
def num_vertices() vertices.size; end
# Returns the number of edges.
def num_edges() edges.size; end
# Utility method to show a string representation of the edges of the graph.
def to_s() edges.to_s; end
# Equality is defined to be same set of edges and directed?
def eql?(g)
return false unless g.kind_of? GRATR::Graph
(g.directed? == self.directed?) and
(vertices.sort == g.vertices.sort) and
(g.edges.sort == edges.sort)
end
# Synonym for eql?
def ==(rhs) eql?(rhs); end
# Merge another graph into this one
def merge(other)
other.vertices.each {|v| add_vertex!(v) }
other.edges.each {|e| add_edge!(e) }
other.edges.each {|e| add_edge!(e.reverse) } if directed? and !other.directed?
self
end
# A synonym for merge, that doesn't modify the current graph
def +(other)
result = self.class.new(self)
case other
when GRATR::Graph : result.merge(other)
when GRATR::Edge : result.add_edge!(other)
else result.add_vertex!(other)
end
end
# Remove all vertices in the specified right hand side graph
def -(other)
case other
when GRATR::Graph : induced_subgraph(vertices - other.vertices)
when GRATR::Edge : self.class.new(self).remove_edge!(other)
else self.class.new(self).remove_vertex!(other)
end
end
# A synonym for add_edge!
def <<(edge) add_edge!(edge); end
# Return the complement of the current graph
def complement
vertices.inject(self.class.new) do |a,v|
a.add_vertex!(v)
vertices.each {|v2| a.add_edge!(v,v2) unless edge?(v,v2) }; a
end
end
# Given an array of vertices return the induced subgraph
def induced_subgraph(v)
edges.inject(self.class.new) do |a,e|
( v.include?(e.source) and v.include?(e.target) ) ? (a << e) : a
end;
end
def inspect
l = vertices.select {|v| self[v]}.map {|u| "vertex_label_set(#{u.inspect},#{self[u].inspect})"}.join('.')
self.class.to_s + '[' + edges.map {|e| e.inspect}.join(', ') + ']' + (l ? '.'+l : '')
end
private
def edge_convert(*args) args[0].kind_of?(GRATR::Edge) ? args[0] : edge_class[*args]; end
end # Graph
end # GRATR
syntax highlighted by Code2HTML, v. 0.9.1