Skip to content

Subgraph #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions lib/bitgraph.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ defmodule BitGraph do
Map.put(graph, :adjacency, Adjacency.copy(adjacency, graph.edges))
end

@doc """
Creates a subgraph on `subgraph_vertices`
"""
def subgraph(graph, subgraph_vertices) do
Enum.reduce(BitGraph.vertices(graph), copy(graph),
fn existing_vertex, acc ->
if existing_vertex not in subgraph_vertices do
BitGraph.delete_vertex(acc, existing_vertex)
else
acc
end
end
)
end

def default_opts() do
[max_vertices: 1024]
end
Expand Down Expand Up @@ -97,10 +112,8 @@ defmodule BitGraph do
from_index = V.get_vertex_index(graph, from)
to_index = V.get_vertex_index(graph, to)
from_index && to_index &&
(
E.delete_edge(graph, from_index, to_index)
Map.update(graph, :edges, %{}, fn edges -> Map.delete(edges, {from_index, to_index}) end)
) || graph
|| graph
end

def delete_edges(graph, edges) do
Expand Down Expand Up @@ -140,6 +153,10 @@ defmodule BitGraph do
out_edges(graph, vertex, fn _from, to, graph -> V.get_vertex(graph, to) end)
end

def neighbors(graph, vertex) do
MapSet.union(out_neighbors(graph, vertex), in_neighbors(graph, vertex))
end

def out_edges(graph, vertex, edge_fun \\ &default_edge_info/3) do
edges_impl(graph, V.get_vertex_index(graph, vertex), edge_fun, :out_edges)
end
Expand All @@ -148,6 +165,10 @@ defmodule BitGraph do
in_neighbors(graph, vertex) |> MapSet.size()
end

def degree(graph, vertex) do
in_degree(graph, vertex) + out_degree(graph, vertex)
end

def out_degree(graph, vertex) do
out_neighbors(graph, vertex) |> MapSet.size()
end
Expand All @@ -156,7 +177,10 @@ defmodule BitGraph do
neighbor_indices = neighbors_impl(graph, vertex_index, direction)
Enum.reduce(neighbor_indices, MapSet.new(), fn neighbor_index, acc ->
{from_vertex, to_vertex} = edge_vertices(vertex_index, neighbor_index, direction)
MapSet.put(acc, edge_info_fun.(from_vertex, to_vertex, graph))
case edge_info_fun.(from_vertex, to_vertex, graph) do
nil -> acc
edge_info -> MapSet.put(acc, edge_info)
end
end)
end

Expand Down
20 changes: 10 additions & 10 deletions lib/edge.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,21 @@ defmodule BitGraph.E do
in_neighbors(graph, vertex) |> MapSet.size()
end

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

def delete_edges(%{edges: edges, adjacency: adjacency} = graph, vertex) when is_integer(vertex) do
Enum.reduce(Adjacency.row(adjacency, vertex), edges, fn out_neighbor, acc ->
Adjacency.clear(adjacency, vertex, out_neighbor)
Map.delete(acc, {vertex, out_neighbor})
def delete_edges(%{adjacency: adjacency} = graph, vertex) when is_integer(vertex) do
Enum.reduce(Adjacency.row(adjacency, vertex), graph, fn out_neighbor, acc ->
delete_edge(acc, vertex, out_neighbor)
end)
|> then(fn edges1 ->
edges2 = Enum.reduce(Adjacency.column(adjacency, vertex), edges1, fn in_neighbor, acc ->
Adjacency.clear(adjacency, in_neighbor, vertex)
Map.delete(acc, {in_neighbor, vertex})
|> then(fn graph ->
Enum.reduce(Adjacency.column(adjacency, vertex), graph, fn in_neighbor, acc ->
delete_edge(acc, in_neighbor, vertex)
end)
Map.put(graph, :edges, edges2)
end)
end
end
33 changes: 33 additions & 0 deletions test/bitgraph_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,37 @@ defmodule BitGraphTest do
end

end

test "subgraph" do
graph = BitGraph.new() |> BitGraph.add_vertices([:a, :b, :c, :d]) |> BitGraph.add_edges(
[
{:a, :b}, {:a, :c}, {:a, :d},
{:b, :c}, {:b, :d},
{:c, :d}
])

subgraph = BitGraph.subgraph(graph, [:a, :b, :c])
assert_subgraph(subgraph)
## The parent graph is not affected
assert BitGraph.num_vertices(graph) == 4
assert BitGraph.num_edges(graph) == 6
## Removing vertex from parent graph does not affect a detached subgraph
graph2 = BitGraph.delete_vertex(graph, :a)
assert BitGraph.num_vertices(graph2) == 3
assert_subgraph(subgraph)
## Removing vertex from subgraph does not affect the parent graph
_subgraph2 = BitGraph.delete_vertex(subgraph, :a)
assert BitGraph.num_vertices(graph) == 4

end


defp assert_subgraph(subgraph) do
assert Enum.sort(BitGraph.vertices(subgraph)) == Enum.sort([:a, :b, :c])
assert BitGraph.num_edges(subgraph) == 3
assert BitGraph.out_neighbors(subgraph, :a) == MapSet.new([:c, :b])
assert BitGraph.out_neighbors(subgraph, :b) == MapSet.new([:c])
assert BitGraph.in_neighbors(subgraph, :b) == MapSet.new([:a])
assert BitGraph.in_neighbors(subgraph, :c) == MapSet.new([:a, :b])
end
end