Skip to content
This repository has been archived by the owner on Oct 21, 2021. It is now read-only.

Added vertex property inspectors. #107

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
120 changes: 94 additions & 26 deletions doc/source/vertex_edge.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,43 +101,111 @@ Both edge types implement the following methods:

A custom edge type ``E{V}`` which is constructible by ``E(index::Int, s::V, t::V)`` and implements the above methods is usable in the ``VectorIncidenceList`` parametric type. Construct such a list with ``inclist(V,E{V})``, where E and V are your vertex and edge types. See test/inclist.jl for an example.

Properties
--------

In order to link real world problems to the graph algorithms we need
to assign properties such as weight, length, flow color, etc. to the
vertices or edges. There are many ways to assign properties to the
vertices and edges, but the algorithms should not have to worry about
how to access the property.

In order to communicate the properties to the algorithm we pass an
object ``i`` that tells the algorithm how to get the data. The
algorithm then calls ``vertex_property(i, v, g)`` or
``edge_property(i, e, g)`` to get the value of the property. The
julia dispatcher uses the type of ``i`` along with the edge or vertex
and graph types to determine the appropriate method to call.

Vertex Properties
---------------

Many algorithms use a property of a vertex such as amount of a
resource provided or required by that vertex as input. These are
communicated to the code through the following methods:

.. py::function:: vertex_property(i, v, g)
Returns the property of vertex ``v`` in graph ``g``.

.. py::function:: vertex_property_requirement(i, g)
Checks that the graph ``g`` supports the interfaces necessary to
access the vertex property.

.. py::function:: vertex_property_type(i, g)
Returns the type returned by ``vertex_property`` in graph ``g``.

In all of these ``i`` is a type that indicates how the property is to
be obtained, both by influencing which method is chosen and providing
information to that method. The following example implementations are provided:

``Number``: If ``i`` is a ``Number`` then each vertex is given that
value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unclear to me. Each vertex is given which value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I can see below what that means.


``AbstractVector``: If ``i`` is an ``AbstractVector`` then each
vertex ``v`` has value ``i[vertex_index(v,g)]``.

If some other method is desired you can overload the
``vertex_property`` and ``vertex_property_type`` functions.

For example, if your vertex type looked like
```
type MyVertex
index::Int
name::ASCIIString
production::Float64
capacity::Float64
```
and you want to pass the production field to an algorithm. First you
would declare a type for the julia dispatcher to key on (you could
use an existing type, but would probably confuse someone reading the
code.)
```
type ProductionInspector
end
```
and provide implementations of ``vertex_property`` and
``vertex_property_type`` (``vertex_property_requirement`` defaults to
``nothing`` and since we have no requirements, we can leave it as is.)
```
import Graphs: vertex_property, vertex_property_type

vertex_property(i::ProductionInspector,v::MyVertex,g::AbstractGraph)=v.production
vertex_property_type(i::ProductionInspector,v::MyVertex,g::AbstractGraph)=Float64
```
and then pass an instance of ``ProductionInspector`` to the
algorithm. The julia optimizer seems to do a good job of optimizing
away all of the dispatching logic.




Edge Properties
---------------


Many algorithms use a property of an edge such as length, weight,
flow, etc. as input. As the algorithms do not mandate any structure
for the edge types, these edge properties can be passed through to the
algorithm by an ``EdgePropertyInspector``. An
``EdgePropertyInspector`` when passed to the ``edge_property`` method
along with an edge and a graph, will return that property of an edge.

All edge property inspectors should be declared as a subtype of
``AbstractEdgePropertyInspector{T}`` where ``T`` is the type of the
edge property. The edge propery inspector should respond to the
following methods.
for the edge types, these edge properties are
communicated to the code through the following methods (analogous to
the vertex meghods:

.. py::function:: edge_property(i, e, g)

returns the edge property of edge ``e`` in graph ``g`` selected by
inspector ``i``.
Returns the property of edge ``e`` in graph ``g``.

.. py::function:: edge_property_requirement(i, g)
Checks that the graph ``g`` supports the interfaces necessary to
access the edge property.

checks that graph ``g`` implements the interface(s) necessary for
inspector ``i``
.. py::function:: vertex_property_type(i, g)
Returns the type returned by ``edge_property`` in graph ``g``.

Three edge property inspectors are provided
``ConstantEdgePropertyInspector``, ``VectorEdgePropertyInspector`` and
``AttributeEdgePropertyInspector``.
In all of these ``i`` is a type that indicates how the property is to
be obtained, both by influencing whish method is chosen and providing
information to that method. The following example implementations are provided:

``ConstantEdgePropertyInspector(c)`` constructs an edge property
inspector that returns the constant ``c`` for each edge.
``Number``: If ``i`` is a ``Number`` then each edge is given that
value.

``VectorEdgePropertyInspector(v)`` constructs an edge property
inspector that returns ``v[edge_index(e, g)]``. It requires that
``g`` implement the ``edge_map`` interface.
``AbstractVector``: If ``i`` is an ``AbstractVector`` then each
edge ``e`` has value ``i[vertex_index(e,g)]``.

``AttributeEdgePropertyInspector(name)`` constructs an edge property
inspector that returns the named attribute from an ``ExEdge``.
``AttributeEdgePropertyInspector`` requires that the graph implements
the ``edge_map`` interface.
8 changes: 5 additions & 3 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ module Graphs

add_edge!, add_vertex!, add_edges!, add_vertices!,

AbstractEdgePropertyInspector, VectorEdgePropertyInspector,
ConstantEdgePropertyInspector, AttributeEdgePropertyInspector,
edge_property, edge_property_requirement,
edge_property_requirement, edge_property, edge_property_type,
vertex_property_requirement, vertex_property, vertex_property_type,

AbstractVertexColormap, VectorVertexColormap, HashVertexColormap,
vertex_colormap_requirement,

# edge_list
GenericEdgeList, EdgeList, simple_edgelist, edgelist,
Expand Down
39 changes: 15 additions & 24 deletions src/a_star_spath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,53 @@ using Base.Collections

export shortest_path

function a_star_impl!{V,D}(
function a_star_impl!{V}(
graph::AbstractGraph{V},# the graph
frontier, # an initialized heap containing the active vertices
colormap::Vector{Int}, # an (initialized) color-map to indicate status of vertices
edge_dists::AbstractEdgePropertyInspector{D}, # cost of each edge
heuristic::Function, # heuristic fn (under)estimating distance to target
edge_dists, # cost of each edge
heuristic, # heuristic fn (under)estimating distance to target
t::V) # the end vertex

while !isempty(frontier)
(cost_so_far, path, u) = dequeue!(frontier)
if u == t
return path
end

for edge in out_edges(u, graph)
v = target(edge)
if colormap[v] < 2
colormap[v] = 1
if colormap[vertex_index(v,graph)] < 2
colormap[vertex_index(v,graph)] = 1
new_path = cat(1, path, edge)
path_cost = cost_so_far + edge_property(edge_dists, edge, graph)
enqueue!(frontier,
(path_cost, new_path, v),
path_cost + heuristic(v))
path_cost + vertex_property(heuristic,v, graph))
end
end
colormap[u] = 2
colormap[vertex_index(u,graph)] = 2
end
nothing
end


function shortest_path{V,E,D}(
function shortest_path{V,E}(
graph::AbstractGraph{V,E}, # the graph
edge_dists::AbstractEdgePropertyInspector{D}, # cost of each edge
edge_dists, # cost of each edge
s::V, # the start vertex
t::V, # the end vertex
heuristic::Function = n -> 0)
# heuristic (under)estimating distance to target
heuristic=0)

# heuristic (under)estimating distance to target
D = edge_property_type(edge_dists, graph)
frontier = PriorityQueue{(D,Array{E,1},V),D}()
frontier[(zero(D), E[], s)] = zero(D)
colormap = zeros(Int, num_vertices(graph))
colormap[s] = 1
colormap[vertex_index(s,graph)] = 1
a_star_impl!(graph, frontier, colormap, edge_dists, heuristic, t)
end

function shortest_path{V,E,D}(
graph::AbstractGraph{V,E}, # the graph
edge_dists::Vector{D}, # cost of each edge
s::V, # the start vertex
t::V, # the end vertex
heuristic::Function = n -> 0)
edge_len::AbstractEdgePropertyInspector{D} = VectorEdgePropertyInspector(edge_dists)
shortest_path(graph, edge_len, s, t, heuristic)
end


end

using .AStar
24 changes: 6 additions & 18 deletions src/bellmanford.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ end

function bellman_ford_shortest_paths!{V,D}(
graph::AbstractGraph{V},
edge_dists::AbstractEdgePropertyInspector{D},
edge_dists,
sources::AbstractVector{V},
state::BellmanFordStates{V,D})

Expand Down Expand Up @@ -70,25 +70,19 @@ function bellman_ford_shortest_paths!{V,D}(
end


function bellman_ford_shortest_paths{V,D}(
function bellman_ford_shortest_paths{V}(
graph::AbstractGraph{V},
edge_dists::AbstractEdgePropertyInspector{D},
edge_dists,
sources::AbstractVector{V})
D = edge_property_type(edge_dists, graph)
state = create_bellman_ford_states(graph, D)
bellman_ford_shortest_paths!(graph, edge_dists, sources, state)
end

function bellman_ford_shortest_paths{V,D}(
graph::AbstractGraph{V},
edge_dists::Vector{D},
sources::AbstractVector{V})
edge_inspector = VectorEdgePropertyInspector{D}(edge_dists)
bellman_ford_shortest_paths(graph, edge_inspector, sources)
end

function has_negative_edge_cycle{V, D}(
function has_negative_edge_cycle{V}(
graph::AbstractGraph{V},
edge_dists::AbstractEdgePropertyInspector{D})
edge_dists)
try
bellman_ford_shortest_paths(graph, edge_dists, vertices(graph))
catch e
Expand All @@ -99,9 +93,3 @@ function has_negative_edge_cycle{V, D}(
return false
end

function has_negative_edge_cycle{V, D}(
graph::AbstractGraph{V},
edge_dists::Vector{D})
edge_inspector = VectorEdgePropertyInspector{D}(edge_dists)
has_negative_edge_cycle(graph, edge_inspector)
end
52 changes: 24 additions & 28 deletions src/common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,36 +154,36 @@ next(a::SourceIterator, s::Int) = ((e, s) = next(a.lst, s); (source(e, a.g), s))

#################################################
#
# Edge Length Visitors
# Vertex Property Inspectors
#
################################################

abstract AbstractEdgePropertyInspector{T}
#constant property
vertex_property(x::Number, v, g) = x
vertex_property_requirement(x, v, g) = nothing
vertex_property_type{T<:Number}(x::T, v, g) = T

edge_property_requirement{T, V}(visitor::AbstractEdgePropertyInspector{T}, g::AbstractGraph{V}) = nothing
#vector property
vertex_property{V}(a::AbstractVector, v::V, g::AbstractGraph{V}) = a[vertex_index(v,g)]
vertex_property_requirement{V}(a::AbstractVector, g::AbstractGraph{V}) = @graph_requires g vertex_map
vertex_property_type{T}(a::AbstractVector{T}, g::AbstractGraph) = T

type ConstantEdgePropertyInspector{T} <: AbstractEdgePropertyInspector{T}
value::T
end

edge_property{T}(visitor::ConstantEdgePropertyInspector{T}, e, g) = visitor.value


type VectorEdgePropertyInspector{T} <: AbstractEdgePropertyInspector{T}
values::Vector{T}
end

edge_property{T,V}(visitor::VectorEdgePropertyInspector{T}, e, g::AbstractGraph{V}) = visitor.values[edge_index(e, g)]
#################################################
#
# Edge Property Inspectors
#
################################################

edge_property_requirement{T, V}(visitor::AbstractEdgePropertyInspector{T}, g::AbstractGraph{V}) = @graph_requires g edge_map
#constant edge property
edge_property(x::Number, e, g) = x
edge_property_requirement(x, g) = nothing
edge_property_type{T<:Number}(x::T,g) = T

type AttributeEdgePropertyInspector{T} <: AbstractEdgePropertyInspector{T}
attribute::UTF8String
end
#vector edge property
edge_property{V,E}(a::AbstractVector, e::E, g::AbstractGraph{V,E}) = a[edge_index(e,g)]
edge_property_requirement{V,E}(a::AbstractVector, g::AbstractGraph{V,E}) = @graph_requires g edge_map
edge_property_type{T}(x::AbstractVector{T}, g::AbstractGraph) = T

function edge_property{T}(visitor::AttributeEdgePropertyInspector{T},edge::ExEdge, g)
convert(T,edge.attributes[visitor.attribute])
end
#################################################
#
# convenient functions
Expand Down Expand Up @@ -223,10 +223,10 @@ end

isless{E,W}(a::WeightedEdge{E,W}, b::WeightedEdge{E,W}) = a.weight < b.weight

function collect_weighted_edges{V,E,W}(graph::AbstractGraph{V,E}, weights::AbstractEdgePropertyInspector{W})
function collect_weighted_edges{V,E}(graph::AbstractGraph{V,E}, weights)

edge_property_requirement(weights, graph)

W = edge_property_type(weights, graph)
wedges = Array(WeightedEdge{E,W}, 0)
sizehint(wedges, num_edges(graph))

Expand All @@ -250,7 +250,3 @@ function collect_weighted_edges{V,E,W}(graph::AbstractGraph{V,E}, weights::Abstr
return wedges
end

function collect_weighted_edges{V,E,W}(graph::AbstractGraph{V,E}, weights::AbstractVector{W})
visitor::AbstractEdgePropertyInspector{D} = VectorEdgePropertyInspector(edge_dists)
collect_weighted_edges(graph, visitor)
end
Loading