diff --git a/doc/source/vertex_edge.rst b/doc/source/vertex_edge.rst index c0078c1e..665c11c3 100644 --- a/doc/source/vertex_edge.rst +++ b/doc/source/vertex_edge.rst @@ -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. + +``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. diff --git a/src/Graphs.jl b/src/Graphs.jl index 3be685ff..c2dd336e 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -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, diff --git a/src/a_star_spath.jl b/src/a_star_spath.jl index 2ce36c19..78e6c890 100644 --- a/src/a_star_spath.jl +++ b/src/a_star_spath.jl @@ -17,12 +17,12 @@ 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) @@ -30,49 +30,40 @@ function a_star_impl!{V,D}( 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 diff --git a/src/bellmanford.jl b/src/bellmanford.jl index b1f21065..57fe1753 100644 --- a/src/bellmanford.jl +++ b/src/bellmanford.jl @@ -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}) @@ -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 @@ -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 diff --git a/src/common.jl b/src/common.jl index ebf1a840..92da84ee 100644 --- a/src/common.jl +++ b/src/common.jl @@ -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 @@ -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)) @@ -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 diff --git a/src/dijkstra_spath.jl b/src/dijkstra_spath.jl index ad3c2cd5..2bbc311e 100644 --- a/src/dijkstra_spath.jl +++ b/src/dijkstra_spath.jl @@ -103,7 +103,7 @@ end function process_neighbors!{V,D,Heap,H}( state::DijkstraStates{V,D,Heap,H}, graph::AbstractGraph{V}, - edge_dists::AbstractEdgePropertyInspector{D}, + edge_dists, u::V, du::D, visitor::AbstractDijkstraVisitor) dists::Vector{D} = state.dists @@ -144,7 +144,7 @@ end function dijkstra_shortest_paths!{V, D, Heap, H}( graph::AbstractGraph{V}, # the graph - edge_dists::AbstractEdgePropertyInspector{D}, # distances associated with edges + edge_dists, # distances associated with edges sources::AbstractVector{V}, # the sources visitor::AbstractDijkstraVisitor, # visitor object state::DijkstraStates{V,D,Heap,H}) # the states @@ -203,11 +203,12 @@ function dijkstra_shortest_paths!{V, D, Heap, H}( end -function dijkstra_shortest_paths{V, D}( +function dijkstra_shortest_paths{V}( graph::AbstractGraph{V}, # the graph - edge_len::AbstractEdgePropertyInspector{D}, # distances associated with edges + edge_len, # distances associated with edges sources::AbstractVector{V}; visitor::AbstractDijkstraVisitor=TrivialDijkstraVisitor()) + D = edge_property_type(edge_dists, graph) state = create_dijkstra_states(graph, D) dijkstra_shortest_paths!(graph, edge_len, sources, visitor, state) end @@ -215,22 +216,21 @@ end # Convenient functions -function dijkstra_shortest_paths{V,D}( - graph::AbstractGraph{V}, edge_dists::Vector{D}, s::V; +function dijkstra_shortest_paths{V}( + graph::AbstractGraph{V}, edge_dists, s::V; visitor::AbstractDijkstraVisitor=TrivialDijkstraVisitor()) - - edge_len::AbstractEdgePropertyInspector{D} = VectorEdgePropertyInspector(edge_dists) + D = edge_property_type(edge_dists, graph) state = create_dijkstra_states(graph, D) - dijkstra_shortest_paths!(graph, edge_len, [s], visitor, state) + dijkstra_shortest_paths!(graph, edge_dists, [s], visitor, state) end -function dijkstra_shortest_paths{V,D}( - graph::AbstractGraph{V}, edge_dists::Vector{D}, sources::AbstractVector{V}; +function dijkstra_shortest_paths{V}( + graph::AbstractGraph{V}, edge_dists, sources::AbstractVector{V}; visitor::AbstractDijkstraVisitor=TrivialDijkstraVisitor()) - edge_len::AbstractEdgePropertyInspector{D} = VectorEdgePropertyInspector(edge_dists) + D = edge_property_type(edge_dists, graph) state = create_dijkstra_states(graph, D) - dijkstra_shortest_paths!(graph, edge_len, sources, visitor, state) + dijkstra_shortest_paths!(graph, edge_dists, sources, visitor, state) end function dijkstra_shortest_paths_withlog{V,D}( diff --git a/src/kruskal_mst.jl b/src/kruskal_mst.jl index 8a63b348..251c4c79 100644 --- a/src/kruskal_mst.jl +++ b/src/kruskal_mst.jl @@ -41,7 +41,7 @@ function kruskal_select{V,E,W}( return (re, rw) end -function kruskal_minimum_spantree(graph::AbstractGraph, eweights::AbstractEdgePropertyInspector; K::Integer=1) +function kruskal_minimum_spantree(graph::AbstractGraph, eweights; K::Integer=1) # collect & sort edges @@ -51,9 +51,3 @@ function kruskal_minimum_spantree(graph::AbstractGraph, eweights::AbstractEdgePr # select the tree edges kruskal_select(graph, wedges, K) end - - -function kruskal_minimum_spantree(graph::AbstractGraph, eweights::AbstractVector; K::Integer=1) - visitor::AbstractEdgePropertyInspector = VectorEdgePropertyInspector(eweights) - kruskal_minimum_spantree(graph, visitor, K=K) -end \ No newline at end of file diff --git a/src/prim_mst.jl b/src/prim_mst.jl index 5d68b284..fb844bdd 100644 --- a/src/prim_mst.jl +++ b/src/prim_mst.jl @@ -123,7 +123,7 @@ end function process_neighbors!{V,E,W,Heap,H}( graph::AbstractGraph{V,E}, # the graph - edge_weights::AbstractEdgePropertyInspector{W}, # weights associated with edges + edge_weights, # weights associated with edges visitor::AbstractPrimVisitor, # visitor object u::V, # the vertex whose neighbor to be examined state::PrimStates{V,W,Heap,H}) # the states (created) @@ -164,10 +164,10 @@ end function prim_minimum_spantree!{V,E,W,Heap,H}( graph::AbstractGraph{V,E}, # the graph - edge_weights::AbstractEdgePropertyInspector{W}, # weights associated with edges + edge_weights, # weights associated with edges root::V, # the root vertex visitor::AbstractPrimVisitor, # visitor object - state::PrimStates{V,W,Heap,H}) # the states (created) + state::PrimStates{V,W,Heap,H}) # the states (created) @graph_requires graph vertex_map incidence_list @@ -207,22 +207,21 @@ end function prim_minimum_spantree{V,E,W}( graph::AbstractGraph{V,E}, - edge_weight_vec::Vector{W}, + edge_weights::Vector{W}, root::V) state = create_prim_states(graph, W) visitor = default_prim_visitor(graph, W) - edge_weights = VectorEdgePropertyInspector(edge_weight_vec) prim_minimum_spantree!(graph, edge_weights, root, visitor, state) return (visitor.edges, visitor.weights) end -function prim_minimum_spantree{V,E,W}( +function prim_minimum_spantree{V,E}( graph::AbstractGraph{V,E}, - edge_weights::AbstractEdgePropertyInspector{W}, + edge_weights, root::V) - + W = edge_property_type(edge_weights, graph) state = create_prim_states(graph, W) visitor = default_prim_visitor(graph, W) prim_minimum_spantree!(graph, edge_weights, root, visitor, state) @@ -231,12 +230,11 @@ end function prim_minimum_spantree_withlog{V,E,W}( graph::AbstractGraph{V,E}, - edge_weight_vec::Vector{W}, + edge_weights::Vector{W}, root::V) state = create_prim_states(graph, W) visitor = LogPrimVisitor(STDOUT) - edge_weights = VectorEdgePropertyInspector(edge_weight_vec) prim_minimum_spantree!(graph, edge_weights, root, visitor, state) end diff --git a/test/a_star_spath.jl b/test/a_star_spath.jl index 33394663..5ba1135d 100644 --- a/test/a_star_spath.jl +++ b/test/a_star_spath.jl @@ -43,6 +43,22 @@ for i = 1 : ne eweights1[i] = we[3] end +import Graphs.vertex_property +import Graphs.vertex_property_type + +vertex_property(f::Function, v, g) = f(v) +vertex_property_type(f::Function, g) = Float64 + sp = shortest_path(g1, eweights1, 1, 2, n -> g1_heuristics[n]) edge_numbers = map(e -> edge_index(e, g1), sp) @test edge_numbers == [2, 7, 15, 16] + +g2 = inclist(KeyVertex{Char}) +vs = [add_vertex!(g2, 'a'+i) for i = 0:19] +for i = 1 : ne + we = g1_wedges[i] + add_edge!(g2, vs[we[1]], vs[we[2]]) +end + +sp2 = shortest_path(g2, eweights1, vs[1], vs[2], g1_heuristics) +@test map(e -> edge_index(e, g2), sp2) == [2, 7, 15, 16] diff --git a/test/bellman_test.jl b/test/bellman_test.jl index d0eaf5de..af92f284 100644 --- a/test/bellman_test.jl +++ b/test/bellman_test.jl @@ -2,6 +2,11 @@ using Graphs using Base.Test +import Graphs.edge_property +import Graphs.edge_property_type +import Graphs.source +import Graphs.target +import Graphs.edge_index # g1: the example in CLRS (2nd Ed.) g1 = simple_inclist(5) @@ -42,9 +47,9 @@ immutable MyEdge{V} target::V dist::Float64 end -Graphs.target{V}(e::MyEdge{V}, g::AbstractGraph{V}) = e.target -Graphs.source{V}(e::MyEdge{V}, g::AbstractGraph{V}) = e.source -Graphs.edge_index(e::MyEdge) = e.index +target{V}(e::MyEdge{V}, g::AbstractGraph{V}) = e.target +source{V}(e::MyEdge{V}, g::AbstractGraph{V}) = e.source +edge_index(e::MyEdge) = e.index g2 = inclist([i for i=1:10], MyEdge{Int}) for i = 2:10 @@ -56,17 +61,19 @@ for i = 2:10 end end -type MyEdgePropertyInspector{T} <: AbstractEdgePropertyInspector{T} end +type MyEdgeInspector +end + +Graphs.edge_property(x::MyEdgeInspector, e::MyEdge, g::AbstractGraph) = e.dist +Graphs.edge_property_type(x::MyEdgeInspector, g::AbstractGraph) = Float64 -Graphs.edge_property{T,V}(inspector::MyEdgePropertyInspector{T}, e::MyEdge, g::AbstractGraph{V}) = e.dist -insp = MyEdgePropertyInspector{Float64}() -s2 = bellman_ford_shortest_paths(g2, insp, [1]) +s2 = bellman_ford_shortest_paths(g2, MyEdgeInspector(), [1]) @test s2.dists == [i * 1.0 for i=0:9] @test s2.parents == [i ==0 ? 1 : i for i = 0:9] -@test !has_negative_edge_cycle(g2, insp) +@test !has_negative_edge_cycle(g2, MyEdgeInspector()) add_edge!(g2, MyEdge{Int}(46, 10, 1, -10.0)) -@test has_negative_edge_cycle(g2, insp) -@test_throws NegativeCycleError bellman_ford_shortest_paths(g2, insp, [1]) +@test has_negative_edge_cycle(g2, MyEdgeInspector()) +@test_throws NegativeCycleError bellman_ford_shortest_paths(g2, MyEdgeInspector(), [1]) # g1: the example in CLR[~S] (1st Ed. Figure 25.7) g3 = simple_inclist(5) diff --git a/test/dijkstra.jl b/test/dijkstra.jl index e99c2a0c..3cce6bbd 100644 --- a/test/dijkstra.jl +++ b/test/dijkstra.jl @@ -51,8 +51,14 @@ for (i,v) in enumerate(g1_wedges) add_edge!(g1ex, ed) end -edgel = AttributeEdgePropertyInspector{Float64}("length") -s1ex = dijkstra_shortest_paths(g1ex, edgel, [1]) +import Graphs.edge_property +import Graphs.edge_property_type + +edge_property(a::UTF8String, e::ExEdge, g::AbstractGraph) = convert(Float64,e.attributes[a]) +edge_property_type(a::UTF8String, g::AbstractGraph) = Float64 + + +s1ex = dijkstra_shortest_paths(g1ex, convert(UTF8String,"length"), [1]) @test s1ex.parents == [1, 3, 1, 2, 3] @test s1ex.dists == [0., 8., 5., 9., 7.]