From f9f5324c3304e0af5a8f98313a6fd892ccbba25e Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:20:39 -0800 Subject: [PATCH 01/12] note and rough bipartite property graph definitions --- src/graphs/PropertyGraphs.jl | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index c7b781d71..06f67f646 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -10,6 +10,7 @@ using ...Theories using ..BasicGraphs import ..BasicGraphs: nv, ne, src, tgt, inv, edges, vertices, has_edge, has_vertex, add_edge!, add_edges!, add_vertex!, add_vertices! +using ..BipartiteGraphs # Data types ############ @@ -97,6 +98,51 @@ end @acset_type ReflexiveEdgePropertyGraph(SchReflexiveEdgePropertyGraph, index=[:src,:tgt]) <: AbstractReflexiveEdgePropertyGraph +# bipartite property graph + +# the design behind the standard property graphs is to have a presentation and acset type +# for the graph itself +# a struct then wraps that along with another dict for graph level attributes + +""" Abstract type for bipartite graph with properties. + +Concrete types are [`BipartitePropertyGraph`](@ref). +""" +abstract type AbstractBipartitePropertyGraph{T} end + +@present SchBipartitePropertyGraph <: SchBipartiteGraph begin + Props::AttrType + v₁props::Attr(V₁,Props) + v₂props::Attr(V₂,Props) + e₁₂props::Attr(E₁₂,Props) + e₂₁props::Attr(E₂₁,Props) +end + +@abstract_acset_type __AbstractBipartitePropertyGraph <: HasBipartiteGraph + +const _AbstractBipartitePropertyGraph{T} = __AbstractBipartitePropertyGraph{S, Tuple{Dict{Symbol,T}}} where {S} + +@acset_type __BipartitePropertyGraph(SchBipartitePropertyGraph, index=[:src₁, :src₂, :tgt₁, :tgt₂]) <: __AbstractBipartitePropertyGraph + +const _BipartitePropertyGraph{T} = __BipartitePropertyGraph{Dict{Symbol,T}} + +""" Bipartite graph with properties. + +"Property graphs" are graphs with arbitrary named properties on the graph, +vertices, and edges. They are intended for applications with a large number of +ad-hoc properties. If you have a small number of known properties, it is better +and more efficient to create a specialized C-set type using `@acset_type`. + +See also: [`SymmetricPropertyGraph`](@ref). +""" +struct BipartitePropertyGraph{T,G<:_AbstractBipartitePropertyGraph{T}} <: AbstractBipartitePropertyGraph{T} + graph::G + gprops::Dict{Symbol,T} +end + +BipartitePropertyGraph{T,G}(; kw...) where {T,G<:_AbstractBipartitePropertyGraph{T}} = BipartitePropertyGraph(G(), Dict{Symbol,T}(kw...)) +BipartitePropertyGraph{T}(; kw...) where T = BipartitePropertyGraph{T,_BipartitePropertyGraph{T}}(; kw...) + # Accessors and mutators ######################## From 5a5e5d12ff86b561ef3b84b6c353e956b3c5a2cb Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:07:59 -0800 Subject: [PATCH 02/12] begin impl of basic interface and some tests --- src/graphs/PropertyGraphs.jl | 39 ++++++++++++++++++++++++++++++++++- test/graphs/PropertyGraphs.jl | 17 +++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 06f67f646..223443162 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -3,7 +3,8 @@ export AbstractPropertyGraph, PropertyGraph, SchPropertyGraph, SymmetricPropertyGraph, SchSymmetricPropertyGraph, ReflexiveEdgePropertyGraph, SchReflexivePropertyGraph, gprops, vprops, eprops, get_gprop, get_vprop, get_eprop, - set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops! + set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops!, + AbstractBipartitePropertyGraph, BipartitePropertyGraph, SchBipartitePropertyGraph using ACSets using ...Theories @@ -11,6 +12,12 @@ using ..BasicGraphs import ..BasicGraphs: nv, ne, src, tgt, inv, edges, vertices, has_edge, has_vertex, add_edge!, add_edges!, add_vertex!, add_vertices! using ..BipartiteGraphs +import ..BipartiteGraphs: nv₁, nv₂, vertices₁, vertices₂, ne₁₂, ne₂₁, edges₁₂, edges₂₁, + src₁, src₂, tgt₁, tgt₂, + add_vertex₁!, add_vertex₂!, add_vertices₁!, add_vertices₂!, + rem_vertex₁!, rem_vertex₂!, rem_vertices₁!, rem_vertices₂!, + add_edge₁₂!, add_edge₂₁!, add_edges₁₂!, add_edges₂₁!, + rem_edge₁₂!, rem_edge₂₁!, rem_edges₁₂!, rem_edges₂₁! # Data types ############ @@ -257,6 +264,36 @@ function add_edges!(g::SymmetricPropertyGraph{T}, srcs::AbstractVector{Int}, inv=invs, eprops=eprops) end +# Bipartite property graphs + +@inline nv₁(g::AbstractBipartitePropertyGraph) = nv₁(g.graph) +@inline nv₂(g::AbstractBipartitePropertyGraph) = nv₂(g.graph) +@inline vertices₁(g::AbstractBipartitePropertyGraph) = vertices₁(g.graph) +@inline vertices₂(g::AbstractBipartitePropertyGraph) = vertices₂(g.graph) +@inline nv(g::AbstractBipartitePropertyGraph) = nv(g.graph) +@inline vertices(g::AbstractBipartitePropertyGraph) = vertices(g.graph) + +add_vertex₁!(g::AbstractBipartitePropertyGraph{T}; kw...) where T = + add_vertex₁!(g, Dict{Symbol,T}(kw...)) +add_vertex₁!(g::AbstractBipartitePropertyGraph{T}, d::Dict{Symbol,T}) where T = + add_part!(g.graph, :V₁, v₁props=d) + +add_vertex₂!(g::AbstractBipartitePropertyGraph{T}; kw...) where T = + add_vertex₂!(g, Dict{Symbol,T}(kw...)) +add_vertex₂!(g::AbstractBipartitePropertyGraph{T}, d::Dict{Symbol,T}) where T = + add_part!(g.graph, :V₂, v₂props=d) + +add_vertices₁!(g::AbstractBipartitePropertyGraph{T}, n::Int; kw...) where T = + add_parts!(g.graph, :V₁, n, v₁props=[Dict{Symbol,T}(kw...) for _=1:n]) +add_vertices₂!(g::AbstractBipartitePropertyGraph{T}, n::Int; kw...) where T = + add_parts!(g.graph, :V₂, n, v₂props=[Dict{Symbol,T}(kw...) for _=1:n]) + +# ne₁₂, ne₂₁, edges₁₂, edges₂₁, +# src₁, src₂, tgt₁, tgt₂, +# rem_vertex₁!, rem_vertex₂!, rem_vertices₁!, rem_vertices₂!, +# add_edge₁₂!, add_edge₂₁!, add_edges₁₂!, add_edges₂₁!, +# rem_edge₁₂!, rem_edge₂₁!, rem_edges₁₂!, rem_edges₂₁! + # Constructors from graphs ########################## diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index 235e05d40..65e1ac44a 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -47,4 +47,21 @@ add_edge!(g, 1, 2, c="car") @test eprops(g, 1) == Dict(:c => "car") @test eprops(g, 1) === eprops(g, 2) +# Bipartite property graphs +########################### + +bg = BipartitePropertyGraph{String}() +add_vertex₁!(bg, a="alphonse", b="elric") +add_vertices₁!(bg, 1, a="ed", b="elric") +add_vertex₂!(bg, a="trisha", b="elric") +add_vertex₂!(bg, a="rurouni", b="kenshin") +add_vertices₂!(bg, 1, a="van", b="hohenheim") + +@test nv₁(bg) == 2 +@test nv₂(bg) == 3 +@test nv(bg) == (2,3) +@test vertices₁(bg) == 1:2 +@test vertices₂(bg) == 1:3 +@test vertices(bg) == (vertices₁(bg), vertices₂(bg)) + end From eb116719d06ac6bbfe11c610e8340609b2564fad Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:10:21 -0800 Subject: [PATCH 03/12] finish impl of methods for bipartite property graphs --- src/graphs/PropertyGraphs.jl | 50 ++++++++++++++++++++++++++++------- test/graphs/PropertyGraphs.jl | 7 +++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 223443162..6b22c835f 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -270,29 +270,61 @@ end @inline nv₂(g::AbstractBipartitePropertyGraph) = nv₂(g.graph) @inline vertices₁(g::AbstractBipartitePropertyGraph) = vertices₁(g.graph) @inline vertices₂(g::AbstractBipartitePropertyGraph) = vertices₂(g.graph) +@inline ne₁₂(g::AbstractBipartitePropertyGraph) = ne₁₂(g.graph) +@inline ne₁₂(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = ne₁₂(g.graph, src, tgt) +@inline ne₂₁(g::AbstractBipartitePropertyGraph) = ne₂₁(g.graph) +@inline ne₂₁(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = ne₂₁(g.graph, src, tgt) +@inline edges₁₂(g::AbstractBipartitePropertyGraph) = edges₁₂(g.graph) +@inline edges₁₂(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = edges₁₂(g.graph, src, tgt) +@inline edges₂₁(g::AbstractBipartitePropertyGraph) = edges₂₁(g.graph) +@inline edges₂₁(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = edges₂₁(g.graph, src, tgt) +@inline src₁(g::AbstractBipartitePropertyGraph, args...) = src₁(g.graph, args...) +@inline tgt₂(g::AbstractBipartitePropertyGraph, args...) = tgt₂(g.graph, args...) +@inline src₂(g::AbstractBipartitePropertyGraph, args...) = src₂(g.graph, args...) +@inline tgt₁(g::AbstractBipartitePropertyGraph, args...) = tgt₁(g.graph, args...) +@inline rem_vertex₁!(g::AbstractBipartitePropertyGraph, v::Int; kw...) = rem_vertex₁!(g.graph, v; kw...) +@inline rem_vertex₂!(g::AbstractBipartitePropertyGraph, v::Int; kw...) = rem_vertex₂!(g.graph, v; kw...) +@inline rem_vertices₁!(g::AbstractBipartitePropertyGraph, vs; keep_edges::Bool=false) = rem_vertices₁!(g.graph, vs; keep_edges) +@inline rem_vertices₂!(g::AbstractBipartitePropertyGraph, vs; keep_edges::Bool=false) = rem_vertices₂!(g.graph, vs; keep_edges) +@inline rem_edge₁₂!(g::AbstractBipartitePropertyGraph, e::Int) = rem_edge₁₂!(g.graph, e) +@inline rem_edge₁₂!(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = rem_edge₁₂!(g.graph, src, tgt) +@inline rem_edge₂₁!(g::AbstractBipartitePropertyGraph, e::Int) = rem_edge₂₁!(g.graph, e) +@inline rem_edge₂₁!(g::AbstractBipartitePropertyGraph, src::Int, tgt::Int) = rem_edge₂₁!(g.graph, src, tgt) +@inline rem_edges₁₂!(g::AbstractBipartitePropertyGraph, es) = rem_edges₁₂!(g.graph, es) +@inline rem_edges₂₁!(g::AbstractBipartitePropertyGraph, es) = rem_edges₂₁!(g.graph, es) + @inline nv(g::AbstractBipartitePropertyGraph) = nv(g.graph) @inline vertices(g::AbstractBipartitePropertyGraph) = vertices(g.graph) +@inline ne(g::AbstractBipartitePropertyGraph) = ne(g.graph) +@inline edges(g::AbstractBipartitePropertyGraph) = edges(g.graph) add_vertex₁!(g::AbstractBipartitePropertyGraph{T}; kw...) where T = add_vertex₁!(g, Dict{Symbol,T}(kw...)) add_vertex₁!(g::AbstractBipartitePropertyGraph{T}, d::Dict{Symbol,T}) where T = - add_part!(g.graph, :V₁, v₁props=d) + add_vertex₁!(g.graph, v₁props=d) add_vertex₂!(g::AbstractBipartitePropertyGraph{T}; kw...) where T = add_vertex₂!(g, Dict{Symbol,T}(kw...)) add_vertex₂!(g::AbstractBipartitePropertyGraph{T}, d::Dict{Symbol,T}) where T = - add_part!(g.graph, :V₂, v₂props=d) + add_vertex₂!(g.graph, v₂props=d) add_vertices₁!(g::AbstractBipartitePropertyGraph{T}, n::Int; kw...) where T = - add_parts!(g.graph, :V₁, n, v₁props=[Dict{Symbol,T}(kw...) for _=1:n]) + add_vertices₁!(g.graph, n, v₁props=[Dict{Symbol,T}(kw...) for _=1:n]) add_vertices₂!(g::AbstractBipartitePropertyGraph{T}, n::Int; kw...) where T = - add_parts!(g.graph, :V₂, n, v₂props=[Dict{Symbol,T}(kw...) for _=1:n]) + add_vertices₂!(g.graph, n, v₂props=[Dict{Symbol,T}(kw...) for _=1:n]) + +add_edge₁₂!(g::AbstractBipartitePropertyGraph{T}, src::Int, tgt::Int; kw...) where T = + add_edge₁₂!(g.graph, src, tgt, e₁₂props=Dict{Symbol,T}(kw...)) +add_edge₂₁!(g::AbstractBipartitePropertyGraph{T}, src::Int, tgt::Int; kw...) where T = + add_edge₂₁!(g.graph, src, tgt, e₂₁props=Dict{Symbol,T}(kw...)) + +add_edges₁₂!(g::AbstractBipartitePropertyGraph{T}, srcs::AbstractVector{Int}, + tgts::AbstractVector{Int}; kw...) where T = + add_edges₁₂!(g.graph, srcs, tgts, e₁₂props=[Dict{Symbol,T}(kw...) for _=1:length(srcs)]) -# ne₁₂, ne₂₁, edges₁₂, edges₂₁, -# src₁, src₂, tgt₁, tgt₂, -# rem_vertex₁!, rem_vertex₂!, rem_vertices₁!, rem_vertices₂!, -# add_edge₁₂!, add_edge₂₁!, add_edges₁₂!, add_edges₂₁!, -# rem_edge₁₂!, rem_edge₂₁!, rem_edges₁₂!, rem_edges₂₁! +add_edges₂₁!(g::AbstractBipartitePropertyGraph{T}, srcs::AbstractVector{Int}, + tgts::AbstractVector{Int}; kw...) where T = + add_edges₂₁!(g.graph, srcs, tgts, e₂₁props=[Dict{Symbol,T}(kw...) for _=1:length(srcs)]) # Constructors from graphs ########################## diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index 65e1ac44a..b339a8797 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -53,10 +53,13 @@ add_edge!(g, 1, 2, c="car") bg = BipartitePropertyGraph{String}() add_vertex₁!(bg, a="alphonse", b="elric") add_vertices₁!(bg, 1, a="ed", b="elric") +add_vertices₁!(bg, 2, f="rei", l="ayanami") add_vertex₂!(bg, a="trisha", b="elric") add_vertex₂!(bg, a="rurouni", b="kenshin") add_vertices₂!(bg, 1, a="van", b="hohenheim") +add_edges₁₂!(bg, [1,1], [1,3], rel="parent") + @test nv₁(bg) == 2 @test nv₂(bg) == 3 @test nv(bg) == (2,3) @@ -64,4 +67,8 @@ add_vertices₂!(bg, 1, a="van", b="hohenheim") @test vertices₂(bg) == 1:3 @test vertices(bg) == (vertices₁(bg), vertices₂(bg)) +# test we can add verticies and edges without any properties +# test failure cases, like adding multiple edges with len src != len tgt +# be sure to test removing vertices/edges, might have bugs + end From 3f9e7baa9eb437dbc59031a139cf0ee8c85a0d5d Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:26:21 -0800 Subject: [PATCH 04/12] note to self --- src/graphs/PropertyGraphs.jl | 2 ++ test/graphs/PropertyGraphs.jl | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 6b22c835f..b4bbbd60a 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -326,6 +326,8 @@ add_edges₂₁!(g::AbstractBipartitePropertyGraph{T}, srcs::AbstractVector{Int} tgts::AbstractVector{Int}; kw...) where T = add_edges₂₁!(g.graph, srcs, tgts, e₂₁props=[Dict{Symbol,T}(kw...) for _=1:length(srcs)]) +# implement the vprops/eprops accessors/mutators + # Constructors from graphs ########################## diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index b339a8797..f919225fc 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -58,12 +58,13 @@ add_vertex₂!(bg, a="trisha", b="elric") add_vertex₂!(bg, a="rurouni", b="kenshin") add_vertices₂!(bg, 1, a="van", b="hohenheim") +@test_throws Exception add_edges₁₂!(bg, [1,1], [1,3, 5], rel="parent") add_edges₁₂!(bg, [1,1], [1,3], rel="parent") -@test nv₁(bg) == 2 +@test nv₁(bg) == 4 @test nv₂(bg) == 3 -@test nv(bg) == (2,3) -@test vertices₁(bg) == 1:2 +@test nv(bg) == (4,3) +@test vertices₁(bg) == 1:4 @test vertices₂(bg) == 1:3 @test vertices(bg) == (vertices₁(bg), vertices₂(bg)) From 9a98dea04850552380eeca89621514a69c0b5ffc Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:39:14 -0800 Subject: [PATCH 05/12] unsure on correct type for BipartiteGraph module methods --- src/graphs/PropertyGraphs.jl | 65 ++++++++++++++++++++++++++++++++++- test/graphs/PropertyGraphs.jl | 12 +++++-- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index b4bbbd60a..888e3eb2c 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -266,6 +266,7 @@ end # Bipartite property graphs +# interface from BipartiteGraphs @inline nv₁(g::AbstractBipartitePropertyGraph) = nv₁(g.graph) @inline nv₂(g::AbstractBipartitePropertyGraph) = nv₂(g.graph) @inline vertices₁(g::AbstractBipartitePropertyGraph) = vertices₁(g.graph) @@ -293,6 +294,7 @@ end @inline rem_edges₁₂!(g::AbstractBipartitePropertyGraph, es) = rem_edges₁₂!(g.graph, es) @inline rem_edges₂₁!(g::AbstractBipartitePropertyGraph, es) = rem_edges₂₁!(g.graph, es) +# interface from BasicGraphs @inline nv(g::AbstractBipartitePropertyGraph) = nv(g.graph) @inline vertices(g::AbstractBipartitePropertyGraph) = vertices(g.graph) @inline ne(g::AbstractBipartitePropertyGraph) = ne(g.graph) @@ -326,7 +328,68 @@ add_edges₂₁!(g::AbstractBipartitePropertyGraph{T}, srcs::AbstractVector{Int} tgts::AbstractVector{Int}; kw...) where T = add_edges₂₁!(g.graph, srcs, tgts, e₂₁props=[Dict{Symbol,T}(kw...) for _=1:length(srcs)]) -# implement the vprops/eprops accessors/mutators +# Property graph interface +gprops(g::AbstractBipartitePropertyGraph) = g.gprops + +# # property graph specific interface +# gprops, vprops, eprops, get_gprop, get_vprop, get_eprop, +# set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops! + + +# """ Properties of vertex in a property graph. +# """ +# vprops(g::AbstractPropertyGraph, v) = subpart(g.graph, v, :vprops) + +# """ Properties of edge in a property graph. +# """ +# eprops(g::AbstractPropertyGraph, e) = subpart(g.graph, e, :eprops) + +# """ Get graph-level property of a property graph. +# """ +# get_gprop(g::AbstractPropertyGraph, key::Symbol) = gprops(g)[key] + +# """ Get property of vertex or vertices in a property graph. +# """ +# get_vprop(g::AbstractPropertyGraph, v, key::Symbol) = +# broadcast(v) do v; vprops(g,v)[key] end + +# """ Get property of edge or edges in a property graph. +# """ +# get_eprop(g::AbstractPropertyGraph, e, key::Symbol) = +# broadcast(e) do e; eprops(g,e)[key] end + +# """ Set graph-level property in a property graph. +# """ +# set_gprop!(g::AbstractPropertyGraph, key::Symbol, value) = +# (gprops(g)[key] = value) + +# """ Set property of vertex or vertices in a property graph. +# """ +# set_vprop!(g::AbstractPropertyGraph, v, key::Symbol, value) = +# broadcast(v, value) do v, value; vprops(g,v)[key] = value end + +# """ Set property of edge or edges in a property graph. +# """ +# set_eprop!(g::AbstractPropertyGraph, e, key::Symbol, value) = +# broadcast(e, value) do e, value; eprops(g,e)[key] = value end + +# """ Set multiple graph-level properties in a property graph. +# """ +# set_gprops!(g::AbstractPropertyGraph; kw...) = merge!(gprops(g), kw) +# set_gprops!(g::AbstractPropertyGraph, d::AbstractDict) = merge!(gprops(g), d) + +# """ Set multiple properties of a vertex in a property graph. +# """ +# set_vprops!(g::AbstractPropertyGraph, v::Int; kw...) = merge!(vprops(g,v), kw) +# set_vprops!(g::AbstractPropertyGraph, v::Int, d::AbstractDict) = +# merge!(vprops(g,v), d) + +# """ Set multiple properties of an edge in a property graph. +# """ +# set_eprops!(g::AbstractPropertyGraph, e::Int; kw...) = merge!(eprops(g,e), kw) +# set_eprops!(g::AbstractPropertyGraph, e::Int, d::AbstractDict) = +# merge!(eprops(g,e), d) + # Constructors from graphs ########################## diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index f919225fc..02a5ead9c 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -58,8 +58,10 @@ add_vertex₂!(bg, a="trisha", b="elric") add_vertex₂!(bg, a="rurouni", b="kenshin") add_vertices₂!(bg, 1, a="van", b="hohenheim") -@test_throws Exception add_edges₁₂!(bg, [1,1], [1,3, 5], rel="parent") -add_edges₁₂!(bg, [1,1], [1,3], rel="parent") +@test_throws Exception add_edges₁₂!(bg, [1,1], [1,3, 5], rel="childof") +add_edges₁₂!(bg, [1,1], [1,3], rel="childof") +add_edges₂₁!(bg, [1,3], [1,1], rel="parentof") +add_edge₂₁!(bg, 3, 2, rel="parentof") @test nv₁(bg) == 4 @test nv₂(bg) == 3 @@ -67,6 +69,12 @@ add_edges₁₂!(bg, [1,1], [1,3], rel="parent") @test vertices₁(bg) == 1:4 @test vertices₂(bg) == 1:3 @test vertices(bg) == (vertices₁(bg), vertices₂(bg)) +@test ne(bg) == (2,3) +@test edges(bg) == (1:2, 1:3) + +e = add_edge₁₂!(bg, 1, 2, a="mistake") +rem_edges₁₂!(bg, e) +rem_edge₁₂!(bg, e) # test we can add verticies and edges without any properties # test failure cases, like adding multiple edges with len src != len tgt From 64d77926a7d0b7326688f4a7b1604063653eedc5 Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:14:09 -0800 Subject: [PATCH 06/12] AbstractBipartiteGraph to HasBipartiteGraph in BipartiteGraphs module --- src/graphs/BipartiteGraphs.jl | 16 ++++++++-------- test/graphs/PropertyGraphs.jl | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/graphs/BipartiteGraphs.jl b/src/graphs/BipartiteGraphs.jl index cc3ddb3cf..1d0b7fcc5 100644 --- a/src/graphs/BipartiteGraphs.jl +++ b/src/graphs/BipartiteGraphs.jl @@ -251,7 +251,7 @@ function add_edges₂₁!(g::HasBipartiteGraph, srcs::AbstractVector{Int}, add_parts!(g, :E₂₁, n; src₂=srcs, tgt₁=tgts, kw...) end -function rem_vertices₁!(g::AbstractBipartiteGraph, vs; keep_edges::Bool=false) +function rem_vertices₁!(g::HasBipartiteGraph, vs; keep_edges::Bool=false) if !keep_edges rem_parts!(g, :E₁₂, unique!(sort!(flatten(incident(g, vs, :src₁))))) rem_parts!(g, :E₂₁, unique!(sort!(flatten(incident(g, vs, :tgt₁))))) @@ -259,7 +259,7 @@ function rem_vertices₁!(g::AbstractBipartiteGraph, vs; keep_edges::Bool=false) rem_parts!(g, :V₁, vs) end -function rem_vertices₂!(g::AbstractBipartiteGraph, vs; keep_edges::Bool=false) +function rem_vertices₂!(g::HasBipartiteGraph, vs; keep_edges::Bool=false) if !keep_edges rem_parts!(g, :E₂₁, unique!(sort!(flatten(incident(g, vs, :src₂))))) rem_parts!(g, :E₁₂, unique!(sort!(flatten(incident(g, vs, :tgt₂))))) @@ -269,22 +269,22 @@ end """ Remove edge from V₁ to V₂ in a bipartite graph. """ -rem_edge₁₂!(g::AbstractBipartiteGraph, e::Int) = rem_part!(g, :E₁₂, e) -rem_edge₁₂!(g::AbstractBipartiteGraph, src::Int, tgt::Int) = +rem_edge₁₂!(g::HasBipartiteGraph, e::Int) = rem_part!(g, :E₁₂, e) +rem_edge₁₂!(g::HasBipartiteGraph, src::Int, tgt::Int) = rem_edge₁₂!(g, first(edges₁₂(g, src, tgt))) """ Remove edge from V₁ to V₂ in a bipartite graph. """ -rem_edge₂₁!(g::AbstractBipartiteGraph, e::Int) = rem_part!(g, :E₂₁, e) -rem_edge₂₁!(g::AbstractBipartiteGraph, src::Int, tgt::Int) = +rem_edge₂₁!(g::HasBipartiteGraph, e::Int) = rem_part!(g, :E₂₁, e) +rem_edge₂₁!(g::HasBipartiteGraph, src::Int, tgt::Int) = rem_edge₂₁!(g, first(edges₂₁(g, src, tgt))) """ Remove edges from V₁ to V₂ in a bipartite graph. """ -rem_edges₁₂!(g::AbstractBipartiteGraph, es) = rem_parts!(g, :E₁₂, es) +rem_edges₁₂!(g::HasBipartiteGraph, es) = rem_parts!(g, :E₁₂, es) """ Remove edges from V₂ to V₁ in a bipartite graph. """ -rem_edges₂₁!(g::AbstractBipartiteGraph, es) = rem_parts!(g, :E₂₁, es) +rem_edges₂₁!(g::HasBipartiteGraph, es) = rem_parts!(g, :E₂₁, es) end diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index f919225fc..d0e93a749 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -1,7 +1,7 @@ module TestPropertyGraphs using Test -using Catlab.Graphs.BasicGraphs, Catlab.Graphs.PropertyGraphs +using Catlab.Graphs.BasicGraphs, Catlab.Graphs.PropertyGraphs, Catlab.Graphs.BipartiteGraphs # Property graphs ################# From 08ec0507121d65b67b6da38f8e4137138eac37de Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:32:17 -0800 Subject: [PATCH 07/12] finish public interface, except constructors --- src/graphs/PropertyGraphs.jl | 138 ++++++++++++++++++++++------------ test/graphs/PropertyGraphs.jl | 52 +++++++++++-- 2 files changed, 138 insertions(+), 52 deletions(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 888e3eb2c..d47266c06 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -4,7 +4,10 @@ export AbstractPropertyGraph, PropertyGraph, SchPropertyGraph, ReflexiveEdgePropertyGraph, SchReflexivePropertyGraph, gprops, vprops, eprops, get_gprop, get_vprop, get_eprop, set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops!, - AbstractBipartitePropertyGraph, BipartitePropertyGraph, SchBipartitePropertyGraph + AbstractBipartitePropertyGraph, BipartitePropertyGraph, SchBipartitePropertyGraph, + v₁props, v₂props, e₁₂props, e₂₁props, get_v₁prop, get_v₂prop, + get_e₁₂prop, get_e₂₁prop, set_v₁prop!, set_v₂prop!, set_e₁₂prop!, set_e₂₁prop!, + set_v₁props!, set_v₂props!, set_e₁₂props!, set_e₂₁props! using ACSets using ...Theories @@ -329,67 +332,104 @@ add_edges₂₁!(g::AbstractBipartitePropertyGraph{T}, srcs::AbstractVector{Int} add_edges₂₁!(g.graph, srcs, tgts, e₂₁props=[Dict{Symbol,T}(kw...) for _=1:length(srcs)]) # Property graph interface + +""" Graph-level properties of a bipartite property graph. +""" gprops(g::AbstractBipartitePropertyGraph) = g.gprops -# # property graph specific interface -# gprops, vprops, eprops, get_gprop, get_vprop, get_eprop, -# set_gprop!, set_vprop!, set_eprop!, set_gprops!, set_vprops!, set_eprops! +""" Get graph-level property of a bipartite property graph. +""" +get_gprop(g::AbstractBipartitePropertyGraph, key::Symbol) = gprops(g)[key] +""" Set graph-level property in a bipartite property graph. +""" +set_gprop!(g::AbstractBipartitePropertyGraph, key::Symbol, value) = + (gprops(g)[key] = value) -# """ Properties of vertex in a property graph. -# """ -# vprops(g::AbstractPropertyGraph, v) = subpart(g.graph, v, :vprops) +""" Set multiple graph-level properties in a property graph. +""" +set_gprops!(g::AbstractBipartitePropertyGraph; kw...) = merge!(gprops(g), kw) +set_gprops!(g::AbstractBipartitePropertyGraph, d::AbstractDict) = merge!(gprops(g), d) -# """ Properties of edge in a property graph. -# """ -# eprops(g::AbstractPropertyGraph, e) = subpart(g.graph, e, :eprops) +""" Properties of vertex of type V₁ in a bipartite property graph. +""" +v₁props(g::AbstractBipartitePropertyGraph, v) = subpart(g.graph, v, :v₁props) -# """ Get graph-level property of a property graph. -# """ -# get_gprop(g::AbstractPropertyGraph, key::Symbol) = gprops(g)[key] +""" Properties of vertex of type V₂ in a bipartite property graph. +""" +v₂props(g::AbstractBipartitePropertyGraph, v) = subpart(g.graph, v, :v₂props) -# """ Get property of vertex or vertices in a property graph. -# """ -# get_vprop(g::AbstractPropertyGraph, v, key::Symbol) = -# broadcast(v) do v; vprops(g,v)[key] end +""" Properties of edge from V₁ to V₂ in a bipartite property graph. +""" +e₁₂props(g::AbstractBipartitePropertyGraph, v) = subpart(g.graph, v, :e₁₂props) -# """ Get property of edge or edges in a property graph. -# """ -# get_eprop(g::AbstractPropertyGraph, e, key::Symbol) = -# broadcast(e) do e; eprops(g,e)[key] end +""" Properties of edge from V₂ to V₁ in a bipartite property graph. +""" +e₂₁props(g::AbstractBipartitePropertyGraph, v) = subpart(g.graph, v, :e₂₁props) -# """ Set graph-level property in a property graph. -# """ -# set_gprop!(g::AbstractPropertyGraph, key::Symbol, value) = -# (gprops(g)[key] = value) +""" Get property of vertex or vertices of type V₁ in a bipartite property graph. +""" +get_v₁prop(g::AbstractBipartitePropertyGraph, v, key::Symbol) = + broadcast(v) do v; v₁props(g,v)[key] end -# """ Set property of vertex or vertices in a property graph. -# """ -# set_vprop!(g::AbstractPropertyGraph, v, key::Symbol, value) = -# broadcast(v, value) do v, value; vprops(g,v)[key] = value end +""" Get property of vertex or vertices of type V₂ in a bipartite property graph. +""" +get_v₂prop(g::AbstractBipartitePropertyGraph, v, key::Symbol) = + broadcast(v) do v; v₂props(g,v)[key] end -# """ Set property of edge or edges in a property graph. -# """ -# set_eprop!(g::AbstractPropertyGraph, e, key::Symbol, value) = -# broadcast(e, value) do e, value; eprops(g,e)[key] = value end +""" Get property of edge or edges from V₁ to V₂ in a bipartite property graph. +""" +get_e₁₂prop(g::AbstractBipartitePropertyGraph, e, key::Symbol) = + broadcast(e) do e; e₁₂props(g,e)[key] end -# """ Set multiple graph-level properties in a property graph. -# """ -# set_gprops!(g::AbstractPropertyGraph; kw...) = merge!(gprops(g), kw) -# set_gprops!(g::AbstractPropertyGraph, d::AbstractDict) = merge!(gprops(g), d) +""" Get property of edge or edges from V₂ to V₁ in a bipartite property graph. +""" +get_e₂₁prop(g::AbstractBipartitePropertyGraph, e, key::Symbol) = + broadcast(e) do e; e₂₁props(g,e)[key] end -# """ Set multiple properties of a vertex in a property graph. -# """ -# set_vprops!(g::AbstractPropertyGraph, v::Int; kw...) = merge!(vprops(g,v), kw) -# set_vprops!(g::AbstractPropertyGraph, v::Int, d::AbstractDict) = -# merge!(vprops(g,v), d) +""" Set property of vertex or vertices of type V₁ in a bipartite property graph. +""" +set_v₁prop!(g::AbstractBipartitePropertyGraph, v, key::Symbol, value) = + broadcast(v, value) do v, value; v₁props(g,v)[key] = value end + +""" Set property of vertex or vertices of type V₂ in a bipartite property graph. +""" +set_v₂prop!(g::AbstractBipartitePropertyGraph, v, key::Symbol, value) = + broadcast(v, value) do v, value; v₂props(g,v)[key] = value end + +""" Set property of edge or edges from V₁ to V₂ in a bipartite property graph. +""" +set_e₁₂prop!(g::AbstractBipartitePropertyGraph, e, key::Symbol, value) = + broadcast(e, value) do e, value; e₁₂props(g,e)[key] = value end + +""" Set property of edge or edges from V₂ to V₁ in a bipartite property graph. +""" +set_e₂₁prop!(g::AbstractBipartitePropertyGraph, e, key::Symbol, value) = + broadcast(e, value) do e, value; e₂₁props(g,e)[key] = value end -# """ Set multiple properties of an edge in a property graph. -# """ -# set_eprops!(g::AbstractPropertyGraph, e::Int; kw...) = merge!(eprops(g,e), kw) -# set_eprops!(g::AbstractPropertyGraph, e::Int, d::AbstractDict) = -# merge!(eprops(g,e), d) +""" Set multiple properties of a vertex of type V₁ in a bipartite property graph. +""" +set_v₁props!(g::AbstractBipartitePropertyGraph, v::Int; kw...) = merge!(v₁props(g,v), kw) +set_v₁props!(g::AbstractBipartitePropertyGraph, v::Int, d::AbstractDict) = + merge!(v₁props(g,v), d) + +""" Set multiple properties of a vertex of type V₂ in a bipartite property graph. +""" +set_v₂props!(g::AbstractBipartitePropertyGraph, v::Int; kw...) = merge!(v₂props(g,v), kw) +set_v₂props!(g::AbstractBipartitePropertyGraph, v::Int, d::AbstractDict) = + merge!(v₂props(g,v), d) +""" Set multiple properties of an edge from V₁ to V₂ in a bipartite property graph. +""" +set_e₁₂props!(g::AbstractBipartitePropertyGraph, e::Int; kw...) = merge!(e₁₂props(g,e), kw) +set_e₁₂props!(g::AbstractBipartitePropertyGraph, e::Int, d::AbstractDict) = + merge!(e₁₂props(g,e), d) + +""" Set multiple properties of an edge from V₂ to V₁ in a bipartite property graph. +""" +set_e₂₁props!(g::AbstractBipartitePropertyGraph, e::Int; kw...) = merge!(e₂₁props(g,e), kw) +set_e₂₁props!(g::AbstractBipartitePropertyGraph, e::Int, d::AbstractDict) = + merge!(e₂₁props(g,e), d) # Constructors from graphs ########################## @@ -430,4 +470,8 @@ end SymmetricPropertyGraph{T}(g::HasGraph; gprops...) where T = SymmetricPropertyGraph{T}(g, v->Dict(), e->Dict(); gprops...) +# BipartitePropertyGraph needs 2 constructors, +# 1. from HasBipartiteVertices +# 2. from HasBipartiteGraph + end diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index 4cef07eae..41fbda7fc 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -58,8 +58,9 @@ add_vertex₂!(bg, a="trisha", b="elric") add_vertex₂!(bg, a="rurouni", b="kenshin") add_vertices₂!(bg, 1, a="van", b="hohenheim") -@test_throws Exception add_edges₁₂!(bg, [1,1], [1,3, 5], rel="childof") +@test_throws Exception add_edges₁₂!(bg, [1,1], [1,3,5], rel="childof") add_edges₁₂!(bg, [1,1], [1,3], rel="childof") +@test_throws Exception add_edges₂₁!(bg, [1,3], [1,1,5], rel="parentof") add_edges₂₁!(bg, [1,3], [1,1], rel="parentof") add_edge₂₁!(bg, 3, 2, rel="parentof") @@ -73,11 +74,52 @@ add_edge₂₁!(bg, 3, 2, rel="parentof") @test edges(bg) == (1:2, 1:3) e = add_edge₁₂!(bg, 1, 2, a="mistake") -rem_edges₁₂!(bg, e) rem_edge₁₂!(bg, e) +@test e ∉ edges₁₂(bg) +e = add_edges₁₂!(bg, [1,1], [2,2], a="mistake") +rem_edges₁₂!(bg, e) +@test e ∉ edges₁₂(bg) + +e = add_edge₂₁!(bg, 1, 2, a="mistake") +rem_edge₂₁!(bg, e) +@test e ∉ edges₂₁(bg) +e = add_edges₂₁!(bg, [1,1], [2,2], a="mistake") +rem_edges₂₁!(bg, e) +@test e ∉ edges₂₁(bg) +@test edges(bg) == (1:2, 1:3) + +@test gprops(bg) isa Dict +@test v₁props(bg, 1) == Dict(:a=>"alphonse", :b=>"elric") +@test v₂props(bg, 1) == Dict(:a=>"trisha", :b=>"elric") +@test e₁₂props(bg, 1) == Dict(:rel=>"childof") +@test e₂₁props(bg, 1) == Dict(:rel=>"parentof") +@test get_v₁prop(bg, 1, :a) == "alphonse" +@test get_v₂prop(bg, 1, :a) == "trisha" +@test get_e₁₂prop(bg, 1, :rel) == "childof" +@test get_e₂₁prop(bg, 1, :rel) == "parentof" + +set_v₁prop!(bg, 4, :f, "rei1") +@test get_v₁prop(bg, 4, :f) == "rei1" + +set_v₂prop!(bg, 2, :a, "himura") +@test get_v₂prop(bg, 2, :a) == "himura" + +set_e₁₂prop!(bg, 1, :rel, "childof1") +@test get_e₁₂prop(bg, 1, :rel) == "childof1" + +set_e₂₁prop!(bg, 1, :rel, "parentof1") +@test get_e₂₁prop(bg, 1, :rel) == "parentof1" + +set_v₁prop!(bg, 3:4, :f, "rei") +get_v₁prop(bg, 3:4, :f) == ["rei", "rei"] + +set_v₂prop!(bg, 2, :a, "kenshin") +get_v₂prop(bg, 2, :a) == "kenshin" + +set_e₁₂props!(bg, 1, rel="childof") +@test get_e₁₂prop(bg, 1:2, :rel) == ["childof", "childof"] -# test we can add verticies and edges without any properties -# test failure cases, like adding multiple edges with len src != len tgt -# be sure to test removing vertices/edges, might have bugs +set_e₂₁props!(bg, 1, rel="parentof") +@test get_e₂₁prop(bg, 1:3, :rel) == ["parentof", "parentof", "parentof"] end From ccbca91d3a7c872793f2d8a3453a2381087f2709 Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:05:15 -0800 Subject: [PATCH 08/12] constructors from graphs implemented and tested --- src/graphs/PropertyGraphs.jl | 48 ++++++++++++++++++++++++++++++++--- test/graphs/PropertyGraphs.jl | 33 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index d47266c06..eab969d92 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -470,8 +470,50 @@ end SymmetricPropertyGraph{T}(g::HasGraph; gprops...) where T = SymmetricPropertyGraph{T}(g, v->Dict(), e->Dict(); gprops...) -# BipartitePropertyGraph needs 2 constructors, -# 1. from HasBipartiteVertices -# 2. from HasBipartiteGraph +function BipartitePropertyGraph{T}(g::HasBipartiteVertices, make_v₁props, make_v₂props, + make_e₁₂props; gprops...) where {T} + pg = BipartitePropertyGraph{T}(; gprops...) + add_vertices₁!(pg, nv₁(g)) + add_vertices₂!(pg, nv₂(g)) + add_edges₁₂!(pg, src(g), tgt(g)) + for v in vertices₁(g) + set_v₁props!(pg, v, make_v₁props(v)) + end + for v in vertices₂(g) + set_v₂props!(pg, v, make_v₂props(v)) + end + for e in edges(g) + set_e₁₂props!(pg, e, make_e₁₂props(e)) + end + pg +end + +BipartitePropertyGraph{T}(g::HasBipartiteVertices; gprops...) where T = + BipartitePropertyGraph{T}(g, v₁->Dict(), v₂->Dict(), e₁₂->Dict(); gprops...) + +function BipartitePropertyGraph{T}(g::HasBipartiteGraph, make_v₁props, make_v₂props, + make_e₁₂props, make_e₂₁props; gprops...) where {T} + pg = BipartitePropertyGraph{T}(; gprops...) + add_vertices₁!(pg, nv₁(g)) + add_vertices₂!(pg, nv₂(g)) + add_edges₁₂!(pg, src₁(g), tgt₂(g)) + add_edges₂₁!(pg, src₂(g), tgt₁(g)) + for v in vertices₁(g) + set_v₁props!(pg, v, make_v₁props(v)) + end + for v in vertices₂(g) + set_v₂props!(pg, v, make_v₂props(v)) + end + for e in edges₁₂(g) + set_e₁₂props!(pg, e, make_e₁₂props(e)) + end + for e in edges₂₁(g) + set_e₂₁props!(pg, e, make_e₂₁props(e)) + end + pg +end + +BipartitePropertyGraph{T}(g::HasBipartiteGraph; gprops...) where T = + BipartitePropertyGraph{T}(g, v₁->Dict(), v₂->Dict(), e₁₂->Dict(), e₂₁->Dict(); gprops...) end diff --git a/test/graphs/PropertyGraphs.jl b/test/graphs/PropertyGraphs.jl index 41fbda7fc..7a171b5fe 100644 --- a/test/graphs/PropertyGraphs.jl +++ b/test/graphs/PropertyGraphs.jl @@ -122,4 +122,37 @@ set_e₁₂props!(bg, 1, rel="childof") set_e₂₁props!(bg, 1, rel="parentof") @test get_e₂₁prop(bg, 1:3, :rel) == ["parentof", "parentof", "parentof"] +# constructors from graphs +g = BipartiteGraph(2, 3) +add_edge₁₂!(g, 1, 2) +add_edge₂₁!(g, 1, 1) +add_edges₁₂!(g, [2,2], [3,3]) +add_edges₂₁!(g, [2,3], [1,1]) + +pg = BipartitePropertyGraph{String}(g, a="test") +@test gprops(pg) == Dict{Symbol,String}(:a=>"test") + +@test nv(pg) == (2, 3) +@test (ne₁₂(pg), ne₂₁(pg)) == (3,3) +@test (edges₁₂(pg), edges₂₁(pg)) == (1:3, 1:3) +@test ne(pg) == (3,3) +@test edges(pg) == (1:3, 1:3) + +g = UndirectedBipartiteGraph() +add_vertices₁!(g, 2) +add_vertices₂!(g, 3) +add_edge!(g, 1, 1) +add_edges!(g, [2,2], [2,3]) + +pg = BipartitePropertyGraph{String}(g, a="test") +@test gprops(pg) == Dict{Symbol,String}(:a=>"test") + +@test (nv₁(pg), nv₂(pg)) == (2,3) +@test (vertices₁(pg), vertices₂(pg)) == (1:2, 1:3) +@test nv(pg) == (2,3) +@test vertices(pg) == (1:2, 1:3) +@test ne(pg) == (3,0) +@test edges₁₂(pg) == (1:3) +@test (src₁(pg), tgt₂(pg)) == ([1,2,2], [1,2,3]) + end From 593008f89573a17dd61363b7b6d0d9a2951dd672 Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:39:13 -0800 Subject: [PATCH 09/12] notes on graphviz impl --- src/graphics/GraphvizGraphs.jl | 14 ++++++++++++++ src/graphs/PropertyGraphs.jl | 2 -- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/graphics/GraphvizGraphs.jl b/src/graphics/GraphvizGraphs.jl index 2388c954f..b612d0f47 100644 --- a/src/graphics/GraphvizGraphs.jl +++ b/src/graphics/GraphvizGraphs.jl @@ -340,6 +340,20 @@ const default_subgraph_edge_attrs = Dict(:color => "cornflowerblue") # Bipartite graphs ################## +# new bipartite graphviz implementation: + +# the main thing that people will interact through +# function to_graphviz(g::HasBipartiteVertices; kw...) +# to_graphviz(to_graphviz_property_graph(g; kw...)) +# end + +# the methods for undirected and bipartite +# to_graphviz_property_graph(g::AbstractUndirectedBipartiteGraph) +# to_graphviz_property_graph(g::AbstractBipartiteGraph) + +# given a property graph return a graphviz one +# function to_graphviz(g::AbstractBipartitePropertyGraph)::Graphviz.Graph end + """ Visualize a bipartite graph using Graphviz. Works for both directed and undirected bipartite graphs. Both types of vertices diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index eab969d92..29a596ed4 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -142,8 +142,6 @@ const _BipartitePropertyGraph{T} = __BipartitePropertyGraph{Dict{Symbol,T}} vertices, and edges. They are intended for applications with a large number of ad-hoc properties. If you have a small number of known properties, it is better and more efficient to create a specialized C-set type using `@acset_type`. - -See also: [`SymmetricPropertyGraph`](@ref). """ struct BipartitePropertyGraph{T,G<:_AbstractBipartitePropertyGraph{T}} <: AbstractBipartitePropertyGraph{T} graph::G From 272fc640c7d18e4a36f51d2b16eeb62e26e87f7a Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:47:34 -0800 Subject: [PATCH 10/12] basic graphviz functionality for bipartite property graphs --- src/graphics/GraphvizGraphs.jl | 108 +++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/graphics/GraphvizGraphs.jl b/src/graphics/GraphvizGraphs.jl index b612d0f47..674c05809 100644 --- a/src/graphics/GraphvizGraphs.jl +++ b/src/graphics/GraphvizGraphs.jl @@ -354,6 +354,114 @@ const default_subgraph_edge_attrs = Dict(:color => "cornflowerblue") # given a property graph return a graphviz one # function to_graphviz(g::AbstractBipartitePropertyGraph)::Graphviz.Graph end +# function to_graphviz_property_graph(g::AbstractGraph; +# prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), +# node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), +# node_labels::Union{Symbol,Bool}=false, edge_labels::Union{Symbol,Bool}=false) +# PropertyGraph{Any}(g, v -> node_label(g, node_labels, v), +# e -> edge_label(g, edge_labels, e); +# prog = prog, +# graph = merge!(default_graph_attrs(prog), graph_attrs), +# node = merge!(default_node_attrs(node_labels), node_attrs), +# edge = merge!(default_edge_attrs(prog), edge_attrs), +# ) +# end + +function to_graphviz_property_graph(g::AbstractBipartiteGraph; + prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), + node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), + node_labels::Union{Tuple{Symbol,Symbol},Bool}=false, edge_labels::Union{Tuple{Symbol,Symbol},Bool}=false) + + node1_labels, node2_labels = node_labels isa Tuple ? node_labels : (node_labels, node_labels) + edge12_labels, edge21_labels = edge_labels isa Tuple ? edge_labels : (edge_labels, edge_labels) + + BipartitePropertyGraph{Any}(g, v -> node_label(g, node1_labels, v), v -> node_label(g, node2_labels, v), + e -> edge_label(g, edge12_labels, e), e -> edge_label(g, edge21_labels, e); + prog = prog, + graph = merge!(default_graph_attrs(prog), graph_attrs), + node = merge!(default_node_attrs(node_labels), node_attrs), + edge = merge!(default_edge_attrs(prog), edge_attrs), + ) +end + +function to_graphviz_property_graph(g::AbstractUndirectedBipartiteGraph; + prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), + node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), + node_labels::Union{Tuple{Symbol,Symbol},Bool}=false, edge_labels::Union{Symbol,Bool}=false) + + node1_labels, node2_labels = node_labels isa Tuple ? node_labels : (node_labels, node_labels) + + BipartitePropertyGraph{Any}(g, v -> node_label(g, node1_labels, v), v -> node_label(g, node2_labels, v), + e -> edge_label(g, edge_labels, e); + prog = prog, + graph = merge!(default_graph_attrs(prog), graph_attrs), + node = merge!(default_node_attrs(node_labels), node_attrs), + edge = merge!(default_edge_attrs(prog), edge_attrs), + ) +end + + +function to_graphviz(g::AbstractBipartitePropertyGraph; kw...)::Graphviz.Graph + + stmts, nodes1, nodes2 = bipartite_graphviz_nodes(g; kw...) + + for e in edges₁₂(g) + attrs = merge!(Dict(:constraint => "false"), e₁₂props(g, e)) + push!(stmts, Graphviz.Edge([nodes1[src₁(g,e)], nodes2[tgt₂(g,e)]], attrs)) + end + for e in edges₂₁(g) + attrs = merge!(Dict(:constraint => "false"), e₂₁props(g, e)) + push!(stmts, Graphviz.Edge([nodes2[src₂(g,e)], nodes1[tgt₁(g,e)]], attrs)) + end + + attrs = gprops(g) + Graphviz.Digraph( + "bipartite_graph", + stmts, + prog = get(attrs, :prog, "dot"), + graph_attrs = get(attrs, :graph, Dict()), + node_attrs = get(attrs, :node, Dict()), + edge_attrs = get(attrs, :edge, Dict()) + ) +end + +function bipartite_graphviz_nodes(g::AbstractBipartitePropertyGraph; invis_edges::Bool=true) + V₁, V₂ = vertices₁(g), vertices₂(g) + + # Vertices of type 1. + nodes1 = map(V₁) do v + Graphviz.Node("n1_$v", v₁props(g, v)) + end + edges1 = Graphviz.Edge[] + if invis_edges + for (u, v) in zip(V₁[1:end-1], V₁[2:end]) + push!(edges1, Graphviz.Edge("n1_$u", "n1_$v"; style="invis")) + end + end + cluster1 = Graphviz.Subgraph("cluster_nodes1", [nodes1; edges1]; + graph_attrs=Graphviz.Attributes(:rank => "same")) + + # Vertices of type 2. + nodes2 = map(V₂) do v + Graphviz.Node("n2_$v", v₂props(g, v)) + end + edges2 = Graphviz.Edge[] + if invis_edges + for (u, v) in zip(V₂[1:end-1], V₂[2:end]) + push!(edges2, Graphviz.Edge("n2_$u", "n2_$v"; style="invis")) + end + end + cluster2 = Graphviz.Subgraph("cluster_nodes2", [nodes2; edges2]; + graph_attrs=Graphviz.Attributes(:rank => "same")) + + stmts = Graphviz.Statement[cluster1, cluster2] + return (stmts, map(n -> n.name, nodes1), map(n -> n.name, nodes2)) +end + + +# ------------------------------------------------------------------------------ +# old bipartite graph viz + """ Visualize a bipartite graph using Graphviz. Works for both directed and undirected bipartite graphs. Both types of vertices From 2cbb770e08322357f757eff405a66350fbeb6cd3 Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:09:14 -0800 Subject: [PATCH 11/12] rm commented out code --- src/graphics/GraphvizGraphs.jl | 142 ++++----------------------------- src/graphs/PropertyGraphs.jl | 6 -- 2 files changed, 14 insertions(+), 134 deletions(-) diff --git a/src/graphics/GraphvizGraphs.jl b/src/graphics/GraphvizGraphs.jl index 674c05809..b5ca6576b 100644 --- a/src/graphics/GraphvizGraphs.jl +++ b/src/graphics/GraphvizGraphs.jl @@ -340,32 +340,15 @@ const default_subgraph_edge_attrs = Dict(:color => "cornflowerblue") # Bipartite graphs ################## -# new bipartite graphviz implementation: - -# the main thing that people will interact through -# function to_graphviz(g::HasBipartiteVertices; kw...) -# to_graphviz(to_graphviz_property_graph(g; kw...)) -# end - -# the methods for undirected and bipartite -# to_graphviz_property_graph(g::AbstractUndirectedBipartiteGraph) -# to_graphviz_property_graph(g::AbstractBipartiteGraph) - -# given a property graph return a graphviz one -# function to_graphviz(g::AbstractBipartitePropertyGraph)::Graphviz.Graph end - -# function to_graphviz_property_graph(g::AbstractGraph; -# prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), -# node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), -# node_labels::Union{Symbol,Bool}=false, edge_labels::Union{Symbol,Bool}=false) -# PropertyGraph{Any}(g, v -> node_label(g, node_labels, v), -# e -> edge_label(g, edge_labels, e); -# prog = prog, -# graph = merge!(default_graph_attrs(prog), graph_attrs), -# node = merge!(default_node_attrs(node_labels), node_attrs), -# edge = merge!(default_edge_attrs(prog), edge_attrs), -# ) -# end +""" Convert a bipartite graph to a Graphviz graph. + +A simple default style is applied. For more control over the visual appearance, +first convert the graph to a property graph, define the Graphviz attributes as +needed, and finally convert the property graph to a Graphviz graph. +""" +function to_graphviz(g::HasBipartiteVertices; kw...) + to_graphviz(to_graphviz_property_graph(g); kw...) +end function to_graphviz_property_graph(g::AbstractBipartiteGraph; prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), @@ -400,7 +383,12 @@ function to_graphviz_property_graph(g::AbstractUndirectedBipartiteGraph; ) end +""" Convert a bipartite property graph to a Graphviz graph. +This method is usually more convenient than direct AST manipulation for creating +simple Graphviz graphs. For more advanced features, like nested subgraphs, you +must use the Graphviz AST. +""" function to_graphviz(g::AbstractBipartitePropertyGraph; kw...)::Graphviz.Graph stmts, nodes1, nodes2 = bipartite_graphviz_nodes(g; kw...) @@ -458,108 +446,6 @@ function bipartite_graphviz_nodes(g::AbstractBipartitePropertyGraph; invis_edges return (stmts, map(n -> n.name, nodes1), map(n -> n.name, nodes2)) end - -# ------------------------------------------------------------------------------ -# old bipartite graph viz - -""" Visualize a bipartite graph using Graphviz. - -Works for both directed and undirected bipartite graphs. Both types of vertices -in the bipartite graph become nodes in the Graphviz graph. - -# Arguments -- `prog="dot"`: Graphviz program to use -- `graph_attrs`: Graph-level Graphviz attributes -- `node_attrs`: Node-level Graphviz attributes -- `edge_attrs`: Edge-level Graphviz attributes -- `node_labels=false`: whether to label nodes and if so, which pair of - data attributes to use -- `edge_labels=false`: whether to label edges and if so, which data attribute - (undirected case) or pair of attributes (directed case) to use -- `invis_edge=true`: whether to add invisible edges between vertices of same - type, which ensures that the order of the nodes is preserved. -""" -function to_graphviz(g::AbstractUndirectedBipartiteGraph; - prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), - node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), - node_labels::Union{Tuple{Symbol,Symbol},Bool}=false, - edge_labels::Union{Symbol,Bool}=false, kw...) - stmts, nodes1, nodes2 = bipartite_graphviz_nodes(g; - node_labels=node_labels, kw...) - - for e in edges(g) - attrs = merge!(Dict(:constraint => "false"), edge_label(g, edge_labels, e)) - push!(stmts, Graphviz.Edge([nodes1[src(g,e)], nodes2[tgt(g,e)]], attrs)) - end - - Graphviz.Digraph("bipartite_graph", stmts, prog=prog, - graph_attrs = merge!(default_graph_attrs(prog), graph_attrs), - node_attrs = merge!(default_node_attrs(node_labels), node_attrs), - edge_attrs = merge!(default_edge_attrs(prog), edge_attrs)) -end - -function to_graphviz(g::AbstractBipartiteGraph; - prog::AbstractString="dot", graph_attrs::AbstractDict=Dict(), - node_attrs::AbstractDict=Dict(), edge_attrs::AbstractDict=Dict(), - node_labels::Union{Tuple{Symbol,Symbol},Bool}=false, - edge_labels::Union{Tuple{Symbol,Symbol},Bool}=false, kw...) - stmts, nodes1, nodes2 = bipartite_graphviz_nodes(g; - node_labels=node_labels, kw...) - - edge12_labels, edge21_labels = edge_labels isa Tuple ? edge_labels : - (edge_labels, edge_labels) - for e in edges₁₂(g) - attrs = merge!(Dict(:constraint => "false"), edge_label(g, edge12_labels, e)) - push!(stmts, Graphviz.Edge([nodes1[src₁(g,e)], nodes2[tgt₂(g,e)]], attrs)) - end - for e in edges₂₁(g) - attrs = merge!(Dict(:constraint => "false"), edge_label(g, edge21_labels, e)) - push!(stmts, Graphviz.Edge([nodes2[src₂(g,e)], nodes1[tgt₁(g,e)]], attrs)) - end - - Graphviz.Digraph("bipartite_graph", stmts, prog=prog, - graph_attrs = merge!(default_graph_attrs(prog), graph_attrs), - node_attrs = merge!(default_node_attrs(node_labels), node_attrs), - edge_attrs = merge!(default_edge_attrs(prog), edge_attrs)) -end - -function bipartite_graphviz_nodes(g::HasBipartiteVertices; - node_labels::Union{Tuple{Symbol,Symbol},Bool}=false, - invis_edges::Bool=true) - V₁, V₂ = vertices₁(g), vertices₂(g) - node1_labels, node2_labels = node_labels isa Tuple ? node_labels : - (node_labels, node_labels) - - # Vertices of type 1. - nodes1 = map(V₁) do v - Graphviz.Node("n1_$v", node_label(g, node1_labels, v)) - end - edges1 = Graphviz.Edge[] - if invis_edges - for (u, v) in zip(V₁[1:end-1], V₁[2:end]) - push!(edges1, Graphviz.Edge("n1_$u", "n1_$v"; style="invis")) - end - end - cluster1 = Graphviz.Subgraph("cluster_nodes1", [nodes1; edges1]; - graph_attrs=Graphviz.Attributes(:rank => "same")) - - # Vertices of type 2. - nodes2 = map(V₂) do v - Graphviz.Node("n2_$v", node_label(g, node2_labels, v)) - end - edges2 = Graphviz.Edge[] - if invis_edges - for (u, v) in zip(V₂[1:end-1], V₂[2:end]) - push!(edges2, Graphviz.Edge("n2_$u", "n2_$v"; style="invis")) - end - end - cluster2 = Graphviz.Subgraph("cluster_nodes2", [nodes2; edges2]; - graph_attrs=Graphviz.Attributes(:rank => "same")) - - stmts = Graphviz.Statement[cluster1, cluster2] - (stmts, map(n -> n.name, nodes1), map(n -> n.name, nodes2)) -end - default_node_shape(::Tuple{Symbol,Symbol}) = "ellipse" node_label(g::HasBipartiteVertices, labels::Bool, v::Int) = diff --git a/src/graphs/PropertyGraphs.jl b/src/graphs/PropertyGraphs.jl index 29a596ed4..91b8720cd 100644 --- a/src/graphs/PropertyGraphs.jl +++ b/src/graphs/PropertyGraphs.jl @@ -108,12 +108,6 @@ end @acset_type ReflexiveEdgePropertyGraph(SchReflexiveEdgePropertyGraph, index=[:src,:tgt]) <: AbstractReflexiveEdgePropertyGraph -# bipartite property graph - -# the design behind the standard property graphs is to have a presentation and acset type -# for the graph itself -# a struct then wraps that along with another dict for graph level attributes - """ Abstract type for bipartite graph with properties. Concrete types are [`BipartitePropertyGraph`](@ref). From a212604e89e259792c4a8a8563f11f9fc282f5a9 Mon Sep 17 00:00:00 2001 From: slwu89 <10673535+slwu89@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:41:54 -0800 Subject: [PATCH 12/12] fix invis_edges forwarding and add tests to cover graphviz for finite sets --- src/graphics/GraphvizGraphs.jl | 4 ++-- test/graphics/GraphvizGraphs.jl | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/graphics/GraphvizGraphs.jl b/src/graphics/GraphvizGraphs.jl index b5ca6576b..5d535158a 100644 --- a/src/graphics/GraphvizGraphs.jl +++ b/src/graphics/GraphvizGraphs.jl @@ -346,8 +346,8 @@ A simple default style is applied. For more control over the visual appearance, first convert the graph to a property graph, define the Graphviz attributes as needed, and finally convert the property graph to a Graphviz graph. """ -function to_graphviz(g::HasBipartiteVertices; kw...) - to_graphviz(to_graphviz_property_graph(g); kw...) +function to_graphviz(g::HasBipartiteVertices; invis_edges::Bool=true, kw...) + to_graphviz(to_graphviz_property_graph(g; kw...); invis_edges) end function to_graphviz_property_graph(g::AbstractBipartiteGraph; diff --git a/test/graphics/GraphvizGraphs.jl b/test/graphics/GraphvizGraphs.jl index 109668193..6807f6e04 100644 --- a/test/graphics/GraphvizGraphs.jl +++ b/test/graphics/GraphvizGraphs.jl @@ -234,4 +234,21 @@ gv = to_graphviz(f, draw_codom=true) @test gv.directed @test length(stmts(gv, Graphviz.Subgraph)) == 2 +# Functions between finite sets +############################### + +A = FinSet(4) +B = FinSet(3) +f = FinFunction([1,3,2,2], A, B) + +fv = to_graphviz(f, graph_attrs=Dict(:splines=>"false")) +@test length(stmts(fv, Graphviz.Subgraph)) == 2 +fv1, fv2 = stmts(fv, Graphviz.Subgraph) + +@test length(stmts(fv1, Graphviz.Edge)) == length(A) - 1 +@test length(stmts(fv2, Graphviz.Edge)) == length(B) - 1 +@test length(stmts(fv, Graphviz.Edge)) == 4 +@test length(stmts(fv1, Graphviz.Node)) == length(A) +@test length(stmts(fv2, Graphviz.Node)) == length(B) + end