diff --git a/Project.toml b/Project.toml index 8b72ac7..2a09bbf 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "IndexedFactorGraphs" uuid = "edf0a622-c16b-410a-a162-e53dc16a76ae" authors = ["stecrotti "] -version = "0.1.3" +version = "0.2.0" [deps] FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" diff --git a/README.md b/README.md index 511e00a..7805110 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ FactorGraph{Int64} with 5 variables, 3 factors, and 6 edges ### Graph navigation Given a factor graph with $N$ variables and $M$ factors, variables are indexed by $i\in\{1,\ldots,N\}$, factors are indexed by $a\in\{1,\ldots,M\}$. -Properties of a vertex can be queried by wrapping the vertex index in a `variable` or `factor`. For example, the list of neighbors of variable $i=2$ is found by +Properties of a vertex can be queried by wrapping the vertex index in a `v_vertex` or `f_vertex`. For example, the list of neighbors of variable $i=2$ is found by ```julia julia> ∂i = neighbors(g, variable(2)); @@ -59,7 +59,7 @@ julia> collect(ea) Querying properties of a vertex without specifying whether it's a variable or a factor will throw an error ```julia julia> outedges(g, 3) -ERROR: ArgumentError: Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `variable` or `factor` wrapper instead. +ERROR: ArgumentError: Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `v_vertex` or `f_vertex` wrapper instead. ``` ## See also diff --git a/docs/src/index.md b/docs/src/index.md index fd578a7..f9a51d8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -38,9 +38,9 @@ FactorGraph{Int64} with 5 variables, 3 factors, and 6 edges ### Graph navigation Given a factor graph with $N$ variables and $M$ factors, variables are indexed by $i\in\{1,\ldots,N\}$, factors are indexed by $a\in\{1,\ldots,M\}$. -Properties of a vertex can be queried by wrapping the vertex index in a `variable` or `factor`. For example, the list of neighbors of variable $i=2$ is found by +Properties of a vertex can be queried by wrapping the vertex index in a `v_vertex` or `f_vertex`. For example, the list of neighbors of variable $i=2$ is found by ```julia -julia> ∂i = neighbors(g, variable(2)); +julia> ∂i = neighbors(g, v_vertex(2)); julia> collect(∂i) 2-element Vector{Int64}: @@ -51,7 +51,7 @@ where $1,3$ are to be interpreted as indices of factor vertices. The list of edges adjacent to factor $a=1$ is found by ```julia -julia> ea = inedges(g, factor(1)); +julia> ea = inedges(g, f_vertex(1)); julia> collect(ea) 3-element Vector{IndexedGraphs.IndexedEdge{Int64}}: @@ -63,7 +63,7 @@ julia> collect(ea) Querying properties of a vertex without specifying whether it's a variable or a factor will throw an error ```julia julia> outedges(g, 3) -ERROR: ArgumentError: Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `variable` or `factor` wrapper instead. +ERROR: ArgumentError: Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `v_vertex` or `f_vertex` wrapper instead. ``` ## See also diff --git a/src/IndexedFactorGraphs.jl b/src/IndexedFactorGraphs.jl index c2a797c..05f83cb 100644 --- a/src/IndexedFactorGraphs.jl +++ b/src/IndexedFactorGraphs.jl @@ -2,7 +2,7 @@ module IndexedFactorGraphs using IndexedGraphs: IndexedGraphs, - IndexedEdge, BipartiteIndexedGraph, BipartiteGraphVertex, NullNumber, + IndexedEdge, IndexedGraph, BipartiteIndexedGraph, BipartiteGraphVertex, NullNumber, src, dst, idx, edges, nv, ne, degree, linearindex, inedges, outedges, vertex, nv_left, nv_right, vertex, Left, Right, rand_bipartite_graph, rand_regular_bipartite_graph, rand_bipartite_tree @@ -19,7 +19,7 @@ include("factorgraph.jl") include("generators.jl") include("infinite_regular_factorgraph.jl") -export AbstractFactorGraph, FactorGraph, nvariables, nfactors, variables, factors, factor, variable, +export AbstractFactorGraph, FactorGraph, nvariables, nfactors, eachvariable, eachfactor, f_vertex, v_vertex, pairwise_interaction_graph, neighbors, edges, src, dst, idx, ne, nv, degree, edge_indices, inedges, outedges, diff --git a/src/factorgraph.jl b/src/factorgraph.jl index 3e49fca..fd4e7a3 100644 --- a/src/factorgraph.jl +++ b/src/factorgraph.jl @@ -1,27 +1,27 @@ -const Factor = Left -const Variable = Right +const FactorVertex = Left +const VariableVertex = Right """ FactorGraphVertex A type to represent a vertex in a bipartite graph, to be passed as an argument to [`neighbors`](@ref), [`inedges`](@ref), [`outedges`](@ref), see examples therein. -It is recommended to use the [`variable`](@ref) and [`factor`](@ref) constructors. +It is recommended to use the [`v_vertex`](@ref) and [`f_vertex`](@ref) constructors. """ const FactorGraphVertex = BipartiteGraphVertex """ - factor(a::Integer) + f_vertex(a::Integer) -Wraps index `a` in a container such that other functions like [`neighbors`](@ref), [`inedges`](@ref), [`outedges`](@ref), knowing that it indices a factor node. +Wraps index `a` in a container such that other functions like [`neighbors`](@ref), [`inedges`](@ref), [`outedges`](@ref), knowing that it indices a factor vertex. """ -factor(a::Integer) = vertex(a, Factor) +f_vertex(a::Integer) = vertex(a, FactorVertex) """ - variable(i::Integer) + v_vertex(i::Integer) -Wraps index `i` in a container such that other functions like [`neighbors`](@ref), [`inedges`](@ref), [`outedges`](@ref), knowing that it indices a variable node. +Wraps index `i` in a container such that other functions like [`neighbors`](@ref), [`inedges`](@ref), [`outedges`](@ref), knowing that it indices a variable vertex. """ -variable(i::Integer) = vertex(i, Variable) +v_vertex(i::Integer) = vertex(i, VariableVertex) abstract type AbstractFactorGraph{T} end @@ -61,6 +61,7 @@ function pairwise_interaction_graph(g::AbstractGraph) A = sparse(I, J, K, E, N) FactorGraph(A) end +pairwise_interaction_graph(A::AbstractMatrix) = pairwise_interaction_graph(IndexedGraph(A)) function Base.show(io::IO, g::FactorGraph{T}) where T nfact = nfactors(g) @@ -82,22 +83,23 @@ nvariables(g::FactorGraph) = nv_right(g.g) Return the number of actors vertices in `g`. """ nfactors(g::FactorGraph) = nv_left(g.g) + IndexedGraphs.nv(g::FactorGraph) = nv(g.g) IndexedGraphs.ne(g::FactorGraph) = ne(g.g) """ - variables(g::FactorGraph) + eachvariable(g::FactorGraph) Return a lazy iterator to the indices of variable vertices in `g`. """ -variables(g::FactorGraph) = 1:nvariables(g) +eachvariable(g::FactorGraph) = 1:nvariables(g) """ - factors(g::FactorGraph) + eachfactor(g::FactorGraph) Return a lazy iterator to the indices of factor vertices in `g`. """ -factors(g::FactorGraph) = 1:nfactors(g) +eachfactor(g::FactorGraph) = 1:nfactors(g) """ IndexedGraphs.neighbors(g::FactorGraph, v::FactorGraphVertex) @@ -115,20 +117,20 @@ julia> g = FactorGraph([0 1 1 0; 0 0 1 1]) FactorGraph{Int64} with 4 variables, 3 factors, and 5 edges -julia> collect(neighbors(g, variable(3))) +julia> collect(neighbors(g, v_vertex(3))) 2-element Vector{Int64}: 1 3 -julia> collect(neighbors(g, factor(2))) +julia> collect(neighbors(g, f_vertex(2))) 1-element Vector{Int64}: 1 ``` """ -function IndexedGraphs.neighbors(g::FactorGraph, a::FactorGraphVertex{Factor}) +function IndexedGraphs.neighbors(g::FactorGraph, a::FactorGraphVertex{FactorVertex}) return @view g.g.X.rowval[nzrange(g.g.X, a.i)] end -function IndexedGraphs.neighbors(g::FactorGraph, i::FactorGraphVertex{Variable}) +function IndexedGraphs.neighbors(g::FactorGraph, i::FactorGraphVertex{VariableVertex}) return @view g.g.A.rowval[nzrange(g.g.A, i.i)] end @@ -153,9 +155,9 @@ FactorGraph{Int64} with 4 variables, 3 factors, and 5 edges julia> edgeprops = randn(ne(g)); -julia> indices = (idx(e) for e in outedges(g, variable(3))); +julia> indices = (idx(e) for e in outedges(g, v_vertex(3))); -julia> indices_noalloc = edge_indices(g, variable(3)); +julia> indices_noalloc = edge_indices(g, v_vertex(3)); julia> @assert edgeprops[collect(indices)] == edgeprops[indices_noalloc] @@ -164,10 +166,10 @@ Test Passed Thrown: ArgumentError ``` """ -function edge_indices(g::FactorGraph, a::FactorGraphVertex{Factor}) +function edge_indices(g::FactorGraph, a::FactorGraphVertex{FactorVertex}) return @view g.g.X.nzval[nzrange(g.g.X, a.i)] end -function edge_indices(g::FactorGraph, i::FactorGraphVertex{Variable}) +function edge_indices(g::FactorGraph, i::FactorGraphVertex{VariableVertex}) return nzrange(g.g.A, i.i) end @@ -195,21 +197,21 @@ julia> g = FactorGraph([0 1 1 0; 0 0 1 1]) FactorGraph{Int64} with 4 variables, 3 factors, and 5 edges -julia> collect(inedges(g, factor(2))) +julia> collect(inedges(g, f_vertex(2))) 1-element Vector{IndexedGraphs.IndexedEdge{Int64}}: Indexed Edge 1 => 2 with index 1 -julia> collect(inedges(g, variable(3))) +julia> collect(inedges(g, v_vertex(3))) 2-element Vector{IndexedGraphs.IndexedEdge{Int64}}: Indexed Edge 1 => 3 with index 3 Indexed Edge 3 => 3 with index 4 ``` """ -function IndexedGraphs.inedges(g::FactorGraph, a::FactorGraphVertex{Factor}) +function IndexedGraphs.inedges(g::FactorGraph, a::FactorGraphVertex{FactorVertex}) return (IndexedEdge(i, a.i, id) for (i, id) in zip(neighbors(g, a), edge_indices(g, a))) end -function IndexedGraphs.inedges(g::FactorGraph, i::FactorGraphVertex{Variable}) +function IndexedGraphs.inedges(g::FactorGraph, i::FactorGraphVertex{VariableVertex}) return (IndexedEdge(a, i.i, id) for (a, id) in zip(neighbors(g, i), edge_indices(g, i))) end @@ -230,20 +232,20 @@ julia> g = FactorGraph([0 1 1 0; 0 0 1 1]) FactorGraph{Int64} with 4 variables, 3 factors, and 5 edges -julia> collect(outedges(g, factor(2))) +julia> collect(outedges(g, f_vertex(2))) 1-element Vector{IndexedGraphs.IndexedEdge{Int64}}: Indexed Edge 2 => 1 with index 1 -julia> collect(outedges(g, variable(3))) +julia> collect(outedges(g, v_vertex(3))) 2-element Vector{IndexedGraphs.IndexedEdge{Int64}}: Indexed Edge 3 => 1 with index 3 Indexed Edge 3 => 3 with index 4 ``` """ -function IndexedGraphs.outedges(g::FactorGraph, a::FactorGraphVertex{Factor}) +function IndexedGraphs.outedges(g::FactorGraph, a::FactorGraphVertex{FactorVertex}) return (IndexedEdge(a.i, i, id) for (i, id) in zip(neighbors(g, a), edge_indices(g, a))) end -function IndexedGraphs.outedges(g::FactorGraph, i::FactorGraphVertex{Variable}) +function IndexedGraphs.outedges(g::FactorGraph, i::FactorGraphVertex{VariableVertex}) return (IndexedEdge(i.i, a, id) for (a, id) in zip(neighbors(g, i), edge_indices(g, i))) end @@ -282,7 +284,7 @@ end for method in [:(IndexedGraphs.degree), :(IndexedGraphs.inedges), :(IndexedGraphs.outedges), :(IndexedGraphs.neighbors), :(edge_indices)] @eval begin function $method(::AbstractFactorGraph, ::Integer) - return throw(ArgumentError("Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `variable` or `factor` wrapper instead.\n")) + return throw(ArgumentError("Properties of a vertex of an `AbstractFactorGraph` such as degree, neighbors, etc. cannot be accessed by an integer. Use a `v_vertex` or `f_vertex` wrapper instead.\n")) end end end diff --git a/src/infinite_regular_factorgraph.jl b/src/infinite_regular_factorgraph.jl index f0823e5..20f6e4b 100644 --- a/src/infinite_regular_factorgraph.jl +++ b/src/infinite_regular_factorgraph.jl @@ -13,7 +13,7 @@ struct InfiniteRegularFactorGraph{T<:Integer} <: AbstractFactorGraph{T} Construct an `InfiniteRegularFactorGraph` with variable degree `kᵢ` and factor degree `kₐ` """ function InfiniteRegularFactorGraph(kᵢ::T, kₐ::T) where {T<:Integer} - kᵢ > 0 || throw(ArgumentError("Factor degree must be positive, got $kᵢ")) + kᵢ > 0 || throw(ArgumentError("Variable degree must be positive, got $kᵢ")) kₐ > 0 || throw(ArgumentError("Factor degree must be positive, got $kₐ")) return new{T}(kᵢ, kₐ) end @@ -27,31 +27,31 @@ nvariables(::InfiniteRegularFactorGraph) = 1 nfactors(::InfiniteRegularFactorGraph) = 1 Graphs.ne(::InfiniteRegularFactorGraph) = 1 Graphs.edges(::InfiniteRegularFactorGraph) = (IndexedEdge(1,1,1) for _ in 1:1) -variables(::InfiniteRegularFactorGraph) = 1:1 -factors(::InfiniteRegularFactorGraph) = 1:1 +eachvariable(::InfiniteRegularFactorGraph) = 1:1 +eachfactor(::InfiniteRegularFactorGraph) = 1:1 IndexedGraphs.degree(g::InfiniteRegularFactorGraph, v::FactorGraphVertex) = length(neighbors(g, v)) -function IndexedGraphs.neighbors(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Factor}) +function IndexedGraphs.neighbors(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{FactorVertex}) return Fill(1, g.kₐ) end -function IndexedGraphs.neighbors(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Variable}) +function IndexedGraphs.neighbors(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{VariableVertex}) return Fill(1, g.kᵢ) end -edge_indices(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Factor}) = Fill(1, g.kₐ) -edge_indices(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Variable}) = Fill(1, g.kᵢ) +edge_indices(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{FactorVertex}) = Fill(1, g.kₐ) +edge_indices(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{VariableVertex}) = Fill(1, g.kᵢ) -function IndexedGraphs.inedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Factor}) +function IndexedGraphs.inedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{FactorVertex}) return (IndexedEdge(1,1,1) for _ in 1:g.kₐ) end -function IndexedGraphs.inedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Variable}) +function IndexedGraphs.inedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{VariableVertex}) return (IndexedEdge(1,1,1) for _ in 1:g.kᵢ) end -function IndexedGraphs.outedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Factor}) +function IndexedGraphs.outedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{FactorVertex}) return (IndexedEdge(1,1,1) for _ in 1:g.kₐ) end -function IndexedGraphs.outedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{Variable}) +function IndexedGraphs.outedges(g::InfiniteRegularFactorGraph, ::FactorGraphVertex{VariableVertex}) return (IndexedEdge(1,1,1) for _ in 1:g.kᵢ) end \ No newline at end of file diff --git a/test/factorgraph.jl b/test/factorgraph.jl index 4821e1c..26c77dc 100644 --- a/test/factorgraph.jl +++ b/test/factorgraph.jl @@ -7,15 +7,15 @@ g = FactorGraph(A) @testset "Basics" begin @test @inferred nfactors(g) == m @test @inferred nvariables(g) == n - @test all(@inferred degree(g, factor(a)) == length(@inferred neighbors(g,factor(a))) for a in factors(g)) - @test all(@inferred degree(g, variable(i)) == length(@inferred neighbors(g,variable(i))) for i in variables(g)) + @test all(@inferred degree(g, f_vertex(a)) == length(@inferred neighbors(g,f_vertex(a))) for a in eachfactor(g)) + @test all(@inferred degree(g, v_vertex(i)) == length(@inferred neighbors(g,v_vertex(i))) for i in eachvariable(g)) @test length(collect(@inferred edges(g))) == @inferred ne(g) - @test all(all(src(e)==a for (e,a) in zip(inedges(g, variable(i)), neighbors(g, variable(i)))) for i in variables(g)) - @test all(all(src(e)==i for (e,i) in zip(inedges(g, factor(a)), neighbors(g, factor(a)))) for a in factors(g)) - @test all(all(dst(e)==a for (e,a) in zip(outedges(g, variable(i)), neighbors(g, variable(i)))) for i in variables(g)) - @test all(all(dst(e)==i for (e,i) in zip(outedges(g, factor(a)), neighbors(g, factor(a)))) for a in factors(g)) + @test all(all(src(e)==a for (e,a) in zip(inedges(g, v_vertex(i)), neighbors(g, v_vertex(i)))) for i in eachvariable(g)) + @test all(all(src(e)==i for (e,i) in zip(inedges(g, f_vertex(a)), neighbors(g, f_vertex(a)))) for a in eachfactor(g)) + @test all(all(dst(e)==a for (e,a) in zip(outedges(g, v_vertex(i)), neighbors(g, v_vertex(i)))) for i in eachvariable(g)) + @test all(all(dst(e)==i for (e,i) in zip(outedges(g, f_vertex(a)), neighbors(g, f_vertex(a)))) for a in eachfactor(g)) @test_throws ArgumentError degree(g, 1) @test_throws ArgumentError neighbors(g, 1) @@ -29,7 +29,7 @@ end end @testset "Broadcasting" begin - ids = [variable(i) for i in rand(1:nvariables(g), 5)] + ids = [v_vertex(i) for i in rand(1:nvariables(g), 5)] @test degree.(g, ids) == degree.((g,), ids) end @@ -38,10 +38,12 @@ end # to test on isolated nodes add_vertex!(g_pairwise) g_factorgraph = pairwise_interaction_graph(g_pairwise) + g_factorgraph_A = pairwise_interaction_graph(adjacency_matrix(g_pairwise)) + @test adjacency_matrix(g_factorgraph) == adjacency_matrix(g_factorgraph) @test all(1:n-1) do i neigs_pairwise = neighbors(g_pairwise, i) - neigs_factorgraph = reduce(vcat, neighbors(g_factorgraph, factor(a)) - for a in neighbors(g_factorgraph, variable(i)); init=Int[]) + neigs_factorgraph = reduce(vcat, neighbors(g_factorgraph, f_vertex(a)) + for a in neighbors(g_factorgraph, v_vertex(i)); init=Int[]) unique!(neigs_factorgraph) filter!(!isequal(i), neigs_factorgraph) neigs_pairwise == neigs_factorgraph @@ -49,11 +51,11 @@ end end @testset "Edge indices" begin - @test all(variables(g)) do i - idx.(collect(inedges(g, variable(i)))) == edge_indices(g, variable(i)) + @test all(eachvariable(g)) do i + idx.(collect(inedges(g, v_vertex(i)))) == edge_indices(g, v_vertex(i)) end - @test all(factors(g)) do a - idx.(collect(inedges(g, factor(a)))) == edge_indices(g, factor(a)) + @test all(eachfactor(g)) do a + idx.(collect(inedges(g, f_vertex(a)))) == edge_indices(g, f_vertex(a)) end end diff --git a/test/infinite_regular_factorgraph.jl b/test/infinite_regular_factorgraph.jl index 2eee6dc..a7999c3 100644 --- a/test/infinite_regular_factorgraph.jl +++ b/test/infinite_regular_factorgraph.jl @@ -3,13 +3,13 @@ kₐ = 4 g = InfiniteRegularFactorGraph(kᵢ, kₐ) @testset "Basics" begin - @test degree(g, variable(100)) == kᵢ - @test degree(g, factor(12)) == kₐ + @test degree(g, v_vertex(100)) == kᵢ + @test degree(g, f_vertex(12)) == kₐ @test nfactors(g) == 1 @test nvariables(g) == 1 @test ne(g) == 1 - @test variables(g) == 1:1 - @test factors(g) == 1:1 + @test eachvariable(g) == 1:1 + @test eachfactor(g) == 1:1 @test all(edges(g)) do (i, j, id) i == 1 && j == 1 && id == 1 end @@ -20,10 +20,10 @@ end end @testset "Edge indices" begin - @test all(variables(g)) do i - idx.(collect(inedges(g, variable(i)))) == edge_indices(g, variable(i)) + @test all(eachvariable(g)) do i + idx.(collect(inedges(g, v_vertex(i)))) == edge_indices(g, v_vertex(i)) end - @test all(factors(g)) do a - idx.(collect(outedges(g, factor(a)))) == edge_indices(g, factor(a)) + @test all(eachfactor(g)) do a + idx.(collect(outedges(g, f_vertex(a)))) == edge_indices(g, f_vertex(a)) end end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index d18f9f1..4881033 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,16 +17,16 @@ Check type inference for functions that are likely to be called in hot loops """ function test_type_inference(g::AbstractFactorGraph) all_edges = @inferred edge_indices(g) - i = rand(rng, @inferred variables(g)) - a = rand(rng, @inferred factors(g)) - di = @inferred degree(g, @inferred variable(i)) - da = @inferred degree(g, @inferred factor(a)) - ∂i = @inferred neighbors(g, variable(i)) - inei = @inferred inedges(g, variable(i)) - outei = @inferred outedges(g, variable(i)) - ∂a = @inferred neighbors(g, factor(a)) - inea = @inferred inedges(g, factor(a)) - outea = @inferred outedges(g, factor(a)) + i = rand(rng, @inferred eachvariable(g)) + a = rand(rng, @inferred eachfactor(g)) + di = @inferred degree(g, @inferred v_vertex(i)) + da = @inferred degree(g, @inferred f_vertex(a)) + ∂i = @inferred neighbors(g, v_vertex(i)) + inei = @inferred inedges(g, v_vertex(i)) + outei = @inferred outedges(g, v_vertex(i)) + ∂a = @inferred neighbors(g, f_vertex(a)) + inea = @inferred inedges(g, f_vertex(a)) + outea = @inferred outedges(g, f_vertex(a)) end include("factorgraph.jl")