Skip to content

Commit 6de8e0a

Browse files
authored
Merge pull request #1 from bokner/subgraph
Subgraph
2 parents 75d1871 + 359a351 commit 6de8e0a

File tree

3 files changed

+71
-14
lines changed

3 files changed

+71
-14
lines changed

lib/bitgraph.ex

+28-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ defmodule BitGraph do
2121
Map.put(graph, :adjacency, Adjacency.copy(adjacency, graph.edges))
2222
end
2323

24+
@doc """
25+
Creates a subgraph on `subgraph_vertices`
26+
"""
27+
def subgraph(graph, subgraph_vertices) do
28+
Enum.reduce(BitGraph.vertices(graph), copy(graph),
29+
fn existing_vertex, acc ->
30+
if existing_vertex not in subgraph_vertices do
31+
BitGraph.delete_vertex(acc, existing_vertex)
32+
else
33+
acc
34+
end
35+
end
36+
)
37+
end
38+
2439
def default_opts() do
2540
[max_vertices: 1024]
2641
end
@@ -97,10 +112,8 @@ defmodule BitGraph do
97112
from_index = V.get_vertex_index(graph, from)
98113
to_index = V.get_vertex_index(graph, to)
99114
from_index && to_index &&
100-
(
101115
E.delete_edge(graph, from_index, to_index)
102-
Map.update(graph, :edges, %{}, fn edges -> Map.delete(edges, {from_index, to_index}) end)
103-
) || graph
116+
|| graph
104117
end
105118

106119
def delete_edges(graph, edges) do
@@ -140,6 +153,10 @@ defmodule BitGraph do
140153
out_edges(graph, vertex, fn _from, to, graph -> V.get_vertex(graph, to) end)
141154
end
142155

156+
def neighbors(graph, vertex) do
157+
MapSet.union(out_neighbors(graph, vertex), in_neighbors(graph, vertex))
158+
end
159+
143160
def out_edges(graph, vertex, edge_fun \\ &default_edge_info/3) do
144161
edges_impl(graph, V.get_vertex_index(graph, vertex), edge_fun, :out_edges)
145162
end
@@ -148,6 +165,10 @@ defmodule BitGraph do
148165
in_neighbors(graph, vertex) |> MapSet.size()
149166
end
150167

168+
def degree(graph, vertex) do
169+
in_degree(graph, vertex) + out_degree(graph, vertex)
170+
end
171+
151172
def out_degree(graph, vertex) do
152173
out_neighbors(graph, vertex) |> MapSet.size()
153174
end
@@ -156,7 +177,10 @@ defmodule BitGraph do
156177
neighbor_indices = neighbors_impl(graph, vertex_index, direction)
157178
Enum.reduce(neighbor_indices, MapSet.new(), fn neighbor_index, acc ->
158179
{from_vertex, to_vertex} = edge_vertices(vertex_index, neighbor_index, direction)
159-
MapSet.put(acc, edge_info_fun.(from_vertex, to_vertex, graph))
180+
case edge_info_fun.(from_vertex, to_vertex, graph) do
181+
nil -> acc
182+
edge_info -> MapSet.put(acc, edge_info)
183+
end
160184
end)
161185
end
162186

lib/edge.ex

+10-10
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,21 @@ defmodule BitGraph.E do
5050
in_neighbors(graph, vertex) |> MapSet.size()
5151
end
5252

53-
def delete_edge(%{adjacency: adjacency} = _graph, from, to) when is_integer(from) and is_integer(to) do
53+
def delete_edge(%{adjacency: adjacency, edges: edges} = graph, from, to) when is_integer(from) and is_integer(to) do
5454
Adjacency.clear(adjacency, from, to)
55+
edges
56+
|> Map.delete({from, to})
57+
|> then(fn updated_edges -> Map.put(graph, :edges, updated_edges) end)
5558
end
5659

57-
def delete_edges(%{edges: edges, adjacency: adjacency} = graph, vertex) when is_integer(vertex) do
58-
Enum.reduce(Adjacency.row(adjacency, vertex), edges, fn out_neighbor, acc ->
59-
Adjacency.clear(adjacency, vertex, out_neighbor)
60-
Map.delete(acc, {vertex, out_neighbor})
60+
def delete_edges(%{adjacency: adjacency} = graph, vertex) when is_integer(vertex) do
61+
Enum.reduce(Adjacency.row(adjacency, vertex), graph, fn out_neighbor, acc ->
62+
delete_edge(acc, vertex, out_neighbor)
6163
end)
62-
|> then(fn edges1 ->
63-
edges2 = Enum.reduce(Adjacency.column(adjacency, vertex), edges1, fn in_neighbor, acc ->
64-
Adjacency.clear(adjacency, in_neighbor, vertex)
65-
Map.delete(acc, {in_neighbor, vertex})
64+
|> then(fn graph ->
65+
Enum.reduce(Adjacency.column(adjacency, vertex), graph, fn in_neighbor, acc ->
66+
delete_edge(acc, in_neighbor, vertex)
6667
end)
67-
Map.put(graph, :edges, edges2)
6868
end)
6969
end
7070
end

test/bitgraph_test.exs

+33
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,37 @@ defmodule BitGraphTest do
134134
end
135135

136136
end
137+
138+
test "subgraph" do
139+
graph = BitGraph.new() |> BitGraph.add_vertices([:a, :b, :c, :d]) |> BitGraph.add_edges(
140+
[
141+
{:a, :b}, {:a, :c}, {:a, :d},
142+
{:b, :c}, {:b, :d},
143+
{:c, :d}
144+
])
145+
146+
subgraph = BitGraph.subgraph(graph, [:a, :b, :c])
147+
assert_subgraph(subgraph)
148+
## The parent graph is not affected
149+
assert BitGraph.num_vertices(graph) == 4
150+
assert BitGraph.num_edges(graph) == 6
151+
## Removing vertex from parent graph does not affect a detached subgraph
152+
graph2 = BitGraph.delete_vertex(graph, :a)
153+
assert BitGraph.num_vertices(graph2) == 3
154+
assert_subgraph(subgraph)
155+
## Removing vertex from subgraph does not affect the parent graph
156+
_subgraph2 = BitGraph.delete_vertex(subgraph, :a)
157+
assert BitGraph.num_vertices(graph) == 4
158+
159+
end
160+
161+
162+
defp assert_subgraph(subgraph) do
163+
assert Enum.sort(BitGraph.vertices(subgraph)) == Enum.sort([:a, :b, :c])
164+
assert BitGraph.num_edges(subgraph) == 3
165+
assert BitGraph.out_neighbors(subgraph, :a) == MapSet.new([:c, :b])
166+
assert BitGraph.out_neighbors(subgraph, :b) == MapSet.new([:c])
167+
assert BitGraph.in_neighbors(subgraph, :b) == MapSet.new([:a])
168+
assert BitGraph.in_neighbors(subgraph, :c) == MapSet.new([:a, :b])
169+
end
137170
end

0 commit comments

Comments
 (0)