diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/lib/edge.ex b/lib/edge.ex old mode 100755 new mode 100644 index c1908ac..d6cec16 --- a/lib/edge.ex +++ b/lib/edge.ex @@ -21,7 +21,6 @@ defmodule Graph.Edge do @type edge_opt :: {:weight, integer | float} | {:label, term} - | {:properties, map} @type edge_opts :: [edge_opt] @doc """ @@ -39,14 +38,11 @@ defmodule Graph.Edge do @spec new(Graph.vertex(), Graph.vertex()) :: t @spec new(Graph.vertex(), Graph.vertex(), [edge_opt]) :: t | no_return def new(v1, v2, opts \\ []) when is_list(opts) do - {weight, opts} = Keyword.pop(opts, :weight, 1) - {label, opts} = Keyword.pop(opts, :label) - %__MODULE__{ v1: v1, v2: v2, - weight: weight, - label: label, + weight: Keyword.get(opts, :weight, 1), + label: Keyword.get(opts, :label), properties: Keyword.get(opts, :properties, %{}) } end @@ -55,13 +51,12 @@ defmodule Graph.Edge do def options_to_meta(opts) when is_list(opts) do label = Keyword.get(opts, :label) weight = Keyword.get(opts, :weight, 1) - properties = Keyword.get(opts, :properties, %{}) - case {label, %{weight: weight, properties: properties}} do - {label, %{weight: w} = meta} when is_number(w) -> - {label, meta} + case {label, weight} do + {_, w} = meta when is_number(w) -> + meta - _ -> + {_, _} -> raise ArgumentError, message: "invalid value for :weight, must be an integer" end end diff --git a/lib/graph.ex b/lib/graph.ex old mode 100755 new mode 100644 index 8349484..7e25fd3 --- a/lib/graph.ex +++ b/lib/graph.ex @@ -50,8 +50,7 @@ defmodule Graph do @type edge_index_key :: label | term @type edge_properties :: %{ label: label, - weight: edge_weight, - properties: map + weight: edge_weight } @type edge_value :: %{label => edge_properties()} @type graph_type :: :directed | :undirected @@ -510,12 +509,8 @@ defmodule Graph do edge_meta when is_map(edge_meta) -> v2 = Map.get(vs, v2_id) - for {label, meta_value} <- edge_meta do - Edge.new(v2, v, - label: label, - weight: meta_value.weight, - properties: meta_value.properties - ) + for {label, weight} <- edge_meta do + Edge.new(v2, v, label: label, weight: weight) end end end) @@ -529,12 +524,8 @@ defmodule Graph do edge_meta when is_map(edge_meta) -> v2 = Map.get(vs, v2_id) - for {label, meta_value} <- edge_meta do - Edge.new(v2, v, - label: label, - weight: meta_value.weight, - properties: meta_value.properties - ) + for {label, weight} <- edge_meta do + Edge.new(v, v2, label: label, weight: weight) end end end) @@ -636,8 +627,8 @@ defmodule Graph do v2_id <- vertex_identifier.(v2), edge_key <- {v1_id, v2_id}, {:ok, edge_meta} <- Map.fetch(meta, edge_key), - {:ok, %{weight: weight, properties: properties}} <- Map.fetch(edge_meta, label) do - Edge.new(v1, v2, label: label, weight: weight, properties: properties) + {:ok, weight} <- Map.fetch(edge_meta, label) do + Edge.new(v1, v2, label: label, weight: weight) else _ -> nil @@ -1043,24 +1034,8 @@ defmodule Graph do end edge_meta = Map.get(meta, {v1_id, v2_id}, %{}) - {label, options_meta} = Edge.options_to_meta(opts) - edge_meta = Map.put(edge_meta, label, options_meta) - - g = - if g.multigraph do - edge = Edge.new(v1, v2, label: label, weight: options_meta.weight, properties: opts) - - partition = g.edge_indexer.(edge) - key = {v1_id, partition} - set = Map.get(g.edge_index, key, MapSet.new()) - - %__MODULE__{ - g - | edge_index: Map.put(g.edge_index, key, MapSet.put(set, {v1_id, v2_id})) - } - else - g - end + {label, weight} = Edge.options_to_meta(opts) + edge_meta = Map.put(edge_meta, label, weight) %__MODULE__{ g @@ -1075,7 +1050,7 @@ defmodule Graph do in a few different ways to make it easy to generate graphs succinctly. Edges must be provided as a list of `Edge` structs, `{vertex, vertex}` pairs, or - `{vertex, vertex, edge_opts :: [label: term, weight: integer, properties: map]}`. + `{vertex, vertex, edge_opts :: [label: term, weight: integer]}`. See the docs for `Graph.Edge.new/2` or `Graph.Edge.new/3` for more info on creating Edge structs, and `add_edge/3` for information on edge options. @@ -2267,12 +2242,8 @@ defmodule Graph do Enum.flat_map(v_out, fn v2_id -> v2 = Map.get(vs, v2_id) - Enum.map(Map.get(meta, {v_id, v2_id}), fn {label, edge_meta} -> - Edge.new(v, v2, - label: label, - weight: edge_meta.weight, - properties: edge_meta.properties - ) + Enum.map(Map.get(meta, {v_id, v2_id}), fn {label, weight} -> + Edge.new(v, v2, label: label, weight: weight) end) end) else @@ -2281,37 +2252,6 @@ defmodule Graph do end end - def out_edges( - %__MODULE__{ - vertices: vs, - edges: edges, - multigraph: true, - edge_index: edge_index, - vertex_identifier: vertex_identifier, - edge_indexer: edge_indexer - }, - v, - partition - ) do - v1_id = vertex_identifier.(v) - key = {v1_id, partition} - # only return out_edges for which the index key returns a subset - edge_index - |> Map.get(key, MapSet.new()) - |> Enum.flat_map(fn {_v1_id, v2_id} = edge_key -> - v2 = Map.get(vs, v2_id) - - edges - |> Map.get(edge_key, []) - |> Enum.map(fn {label, edge_meta} -> - Edge.new(v, v2, label: label, weight: edge_meta.weight, properties: edge_meta.properties) - end) - |> Enum.filter(fn edge -> - edge_indexer.(edge) == partition - end) - end) - end - @doc """ Builds a maximal subgraph of `g` which includes all of the vertices in `vs` and the edges which connect them. diff --git a/lib/graph/pathfinding.ex b/lib/graph/pathfinding.ex old mode 100755 new mode 100644 index b21724a..c7f7fa6 --- a/lib/graph/pathfinding.ex +++ b/lib/graph/pathfinding.ex @@ -6,8 +6,7 @@ defmodule Graph.Pathfinding do @type heuristic_fun :: (Graph.vertex() -> integer) - @spec bellman_ford(Graph.t(), Graph.vertex()) :: - %{Graph.vertex() => integer() | :infinity} | nil + @spec bellman_ford(Graph.t, Graph.vertex) :: %{Graph.vertex() => integer() | :infinity} | nil def bellman_ford(g, a), do: Graph.Pathfindings.BellmanFord.call(g, a) @doc """ diff --git a/lib/graph/serializers/dot.ex b/lib/graph/serializers/dot.ex old mode 100755 new mode 100644 index 2266795..0df88c7 --- a/lib/graph/serializers/dot.ex +++ b/lib/graph/serializers/dot.ex @@ -14,9 +14,7 @@ defmodule Graph.Serializers.DOT do defp serialize_nodes(%Graph{vertices: vertices} = g) do Enum.reduce(vertices, "", fn {id, v}, acc -> - acc <> - Serializer.indent(1) <> - "#{id}" <> "[label=" <> Serializer.get_vertex_label(g, id, v) <> "]\n" + acc <> Serializer.indent(1) <> "#{id}" <> "[label=" <> Serializer.get_vertex_label(g, id, v) <> "]\n" end) end diff --git a/lib/graph/utils.ex b/lib/graph/utils.ex old mode 100755 new mode 100644 index 2880961..b5a8f5d --- a/lib/graph/utils.ex +++ b/lib/graph/utils.ex @@ -83,7 +83,7 @@ defmodule Graph.Utils do def edge_weight(%Graph{type: :directed, edges: meta}, a, b) do Map.fetch!(meta, {a, b}) - |> Enum.map(fn {_label, %{weight: weight}} -> weight end) + |> Enum.map(fn {_label, weight} -> weight end) |> Enum.min() end @@ -96,13 +96,13 @@ defmodule Graph.Utils do edge_meta when is_map(edge_meta) -> edge_meta - |> Enum.map(fn {_label, %{weight: weight}} -> weight end) + |> Enum.map(fn {_label, weight} -> weight end) |> Enum.min() end edge_meta when is_map(edge_meta) -> edge_meta - |> Enum.map(fn {_label, %{weight: weight}} -> weight end) + |> Enum.map(fn {_label, weight} -> weight end) |> Enum.min() end end diff --git a/test/graph_test.exs b/test/graph_test.exs old mode 100755 new mode 100644 index e1a9b00..63cb97c --- a/test/graph_test.exs +++ b/test/graph_test.exs @@ -31,39 +31,29 @@ defmodule GraphTest do {:b, :c, weight: 3}, {:b, :a, label: {:complex, :label}} ]) - |> IO.inspect(structs: false) assert Enum.count(Graph.out_edges(graph, :a)) == 3 assert [%Edge{label: :foo}] = Graph.out_edges(graph, :a, :foo) assert [%Edge{label: :foo}] = Graph.in_edges(graph, :b, :foo) assert [%Edge{label: :bar}] = Graph.out_edges(graph, :a, :bar) - assert [%Edge{label: nil}] = Graph.out_edges(graph, :a, nil) + assert [%Edge{label: :nil}] = Graph.out_edges(graph, :a, nil) assert [] == Graph.out_edges(graph, :a, :foobar) end - test "custom edge indexing function" do - graph = - Graph.new(multigraph: true, edge_indexer: fn edge -> edge.weight end) - |> Graph.add_edges([ - {:a, :b}, - {:a, :b, label: :foo}, - {:a, :b, label: :bar}, - {:b, :c, weight: 3}, - {:b, :a, weight: 6} - ]) + test "custom vertex indexing function on edge labels" do - assert Enum.count(Graph.out_edges(graph, :b)) == 2 - assert [%Edge{weight: 6}] = Graph.out_edges(graph, :b, 6) - assert [%Edge{weight: 6}] = Graph.out_edges(graph, :b, 3) end - test "traversal using indexed keys" do + test "traversal using indexed labels" do + end - test "edge properties" do + test "updating edge properties" do + end test "removing edges" do + end end @@ -683,7 +673,7 @@ defmodule GraphTest do end defp build_complex_signed_graph do - Graph.new() + Graph.new |> Graph.add_edge(:a, :b, weight: -1) |> Graph.add_edge(:b, :e, weight: 2) |> Graph.add_edge(:e, :d, weight: -3) diff --git a/test/reducer_test.exs b/test/reducer_test.exs old mode 100755 new mode 100644 index 428024f..713e32f --- a/test/reducer_test.exs +++ b/test/reducer_test.exs @@ -35,19 +35,17 @@ defmodule Graph.Reducer.Test do end test "can walk a graph breadth-first, when the starting points had their in-edges deleted" do - g = - Graph.new() - |> Graph.add_vertices([:a, :b, :c, :d, :e, :f, :g]) - |> Graph.add_edge(:a, :b) - |> Graph.add_edge(:a, :d) - |> Graph.add_edge(:b, :c) - |> Graph.add_edge(:b, :d) - |> Graph.add_edge(:c, :e) - |> Graph.add_edge(:d, :f) - |> Graph.add_edge(:f, :g) - # Add this edge and then remove it - |> Graph.add_edge(:b, :a) - |> Graph.delete_edge(:b, :a) + g = Graph.new + |> Graph.add_vertices([:a, :b, :c, :d, :e, :f, :g]) + |> Graph.add_edge(:a, :b) + |> Graph.add_edge(:a, :d) + |> Graph.add_edge(:b, :c) + |> Graph.add_edge(:b, :d) + |> Graph.add_edge(:c, :e) + |> Graph.add_edge(:d, :f) + |> Graph.add_edge(:f, :g) + |> Graph.add_edge(:b, :a) # Add this edge and then remove it + |> Graph.delete_edge(:b, :a) expected = [:a, :b, :d, :c, :f, :e, :g] assert ^expected = Graph.Reducers.Bfs.map(g, fn v -> v end)