Skip to content

Commit 7bdb117

Browse files
committed
Add constructor and fix traits
1 parent fe2a5bb commit 7bdb117

File tree

5 files changed

+130
-64
lines changed

5 files changed

+130
-64
lines changed

src/directedness.jl

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
function Graphs.is_directed(::MetaGraph{Code,Graph}) where {Code,Graph<:AbstractGraph}
2+
return is_directed(Graph)
3+
end
4+
5+
function Graphs.is_directed(
6+
::Type{<:MetaGraph{Code,Graph}}
7+
) where {Code,Graph<:AbstractGraph}
8+
return is_directed(Graph)
9+
end
10+
111
"""
212
arrange(graph, label_1, label_2)
313
@@ -6,14 +16,14 @@ Sort two vertex labels in a default order (useful to uniquely express undirected
616
function arrange end
717

818
@traitfn function arrange(
9-
::AG::IsDirected, label_1, label_2, _drop...
10-
) where {T,AG<:AbstractGraph{T}}
19+
::MG, label_1, label_2, _drop...
20+
) where {MG <: MetaGraph; IsDirected{MG}}
1121
return label_1, label_2
1222
end
1323

1424
@traitfn function arrange(
15-
::AG::(!IsDirected), label_1, label_2, code_1, code_2
16-
) where {T,AG<:AbstractGraph{T}}
25+
::MG, label_1, label_2, code_1, code_2
26+
) where {MG <: MetaGraph; !IsDirected{MG}}
1727
if code_1 < code_2
1828
(label_1, label_2)
1929
else
@@ -22,8 +32,8 @@ end
2232
end
2333

2434
@traitfn function arrange(
25-
meta_graph::AG::(!IsDirected), label_1, label_2
26-
) where {T,AG<:AbstractGraph{T}}
35+
meta_graph::MG, label_1, label_2
36+
) where {MG <: MetaGraph; !IsDirected{MG}}
2737
return arrange(
2838
meta_graph,
2939
label_1,

src/graphs.jl

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,35 @@ end
1010
function Graphs.edgetype(meta_graph::MetaGraph)
1111
return edgetype(meta_graph.graph)
1212
end
13+
1314
function Graphs.nv(meta_graph::MetaGraph)
1415
return nv(meta_graph.graph)
1516
end
17+
1618
function Graphs.ne(meta_graph::MetaGraph)
1719
return ne(meta_graph.graph)
1820
end
21+
1922
function Graphs.vertices(meta_graph::MetaGraph)
2023
return vertices(meta_graph.graph)
2124
end
25+
2226
function Graphs.edges(meta_graph::MetaGraph)
2327
return edges(meta_graph.graph)
2428
end
2529

2630
function Graphs.has_vertex(meta_graph::MetaGraph, code::Integer)
2731
return has_vertex(meta_graph.graph, code)
2832
end
33+
2934
function Graphs.has_edge(meta_graph::MetaGraph, code_1::Integer, code_2::Integer)
3035
return has_edge(meta_graph.graph, code_1, code_2)
3136
end
3237

3338
function Graphs.inneighbors(meta_graph::MetaGraph, code::Integer)
3439
return inneighbors(meta_graph.graph, code)
3540
end
41+
3642
function Graphs.outneighbors(meta_graph::MetaGraph, code::Integer)
3743
return outneighbors(meta_graph.graph, code)
3844
end
@@ -41,18 +47,6 @@ function Base.issubset(meta_graph::MetaGraph, h::MetaGraph)
4147
return issubset(meta_graph.graph, h.graph)
4248
end
4349

44-
function Graphs.is_directed(
45-
::MetaGraph{Code,Label,Graph}
46-
) where {Code,Label,Graph<:AbstractGraph}
47-
return is_directed(Graph)
48-
end
49-
50-
function Graphs.is_directed(
51-
::Type{<:MetaGraph{Code,Label,Graph}}
52-
) where {Code,Label,Graph<:AbstractGraph}
53-
return is_directed(Graph)
54-
end
55-
5650
## Link between graph codes and metagraph labels
5751

5852
"""
@@ -205,6 +199,22 @@ end
205199

206200
## Miscellaneous
207201

202+
function Base.copy(meta_graph::MetaGraph)
203+
return deepcopy(meta_graph)
204+
end
205+
206+
function Base.zero(meta_graph::MetaGraph)
207+
return MetaGraph(
208+
zero(meta_graph.graph),
209+
empty(meta_graph.vertex_labels),
210+
empty(meta_graph.vertex_properties),
211+
empty(meta_graph.edge_data),
212+
deepcopy(meta_graph.graph_data),
213+
deepcopy(meta_graph.weight_function),
214+
deepcopy(meta_graph.default_weight),
215+
)
216+
end
217+
208218
function Graphs.induced_subgraph(
209219
meta_graph::MetaGraph, vertex_codes::AbstractVector{<:Integer}
210220
)
@@ -222,7 +232,7 @@ function Graphs.induced_subgraph(
222232
return new_graph, code_map
223233
end
224234

225-
@traitfn function Graphs.reverse(meta_graph::MetaGraph::IsDirected)
235+
@traitfn function Graphs.reverse(meta_graph::MG) where {MG <: MetaGraph; IsDirected{MG}}
226236
edge_data = meta_graph.edge_data
227237
reverse_edge_data = empty(edge_data)
228238
for (label_1, label_2) in keys(edge_data)

src/metagraph.jl

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""
22
MetaGraph{
33
Code<:Integer,
4+
Graph<:AbstractGraph{Code},
45
Label,
5-
Graph,
66
VertexData,
77
EdgeData,
88
GraphData,
99
WeightFunction,
10-
Weight<:Real
10+
Weight
1111
} <: AbstractGraph{Code}
1212
1313
A graph type with custom vertex labels containing vertex-, edge- and graph-level metadata.
@@ -19,13 +19,20 @@ It is recommended not to set `Label` to an integer type, so as to avoid confusio
1919
- `graph::Graph`: underlying, data-less graph with vertex indices of type `Code`
2020
- `vertex_labels::Dict{Code,Label}`: dictionary mapping vertex codes to vertex labels
2121
- `vertex_properties::Dict{Label,Tuple{Code,VertexData}}`: dictionary mapping vertex labels to vertex codes & data
22-
- `edge_data::Dict{Tuple{Label,Label},EdgeData}`: dictionary mapping edge labels such as `(label_u, label_v)` to edge metadata
23-
- `graph_data::GraphData`: graph metadata
24-
- `weight_function::WeightFunction`: function defining edge weight from edge metadata
25-
- `default_weight::Weight`: default weight for the edges
22+
- `edge_data::Dict{Tuple{Label,Label},EdgeData}`: dictionary mapping edge labels such as `(label_u, label_v)` to edge data
23+
- `graph_data::GraphData`: global data for the graph object
24+
- `weight_function::WeightFunction`: function computing edge weight from edge data, its output must have the same type as `default_weight`
25+
- `default_weight::Weight`: default weight used when an edge doesn't exist
2626
"""
2727
struct MetaGraph{
28-
Code<:Integer,Label,Graph,VertexData,EdgeData,GraphData,WeightFunction,Weight<:Real
28+
Code<:Integer,
29+
Graph<:AbstractGraph{Code},
30+
Label,
31+
VertexData,
32+
EdgeData,
33+
GraphData,
34+
WeightFunction,
35+
Weight,
2936
} <: AbstractGraph{Code}
3037
graph::Graph
3138
vertex_labels::Dict{Code,Label}
@@ -36,68 +43,107 @@ struct MetaGraph{
3643
default_weight::Weight
3744
end
3845

46+
"""
47+
MetaGraph(
48+
graph,
49+
vertices_description,
50+
edges_description,
51+
graph_data=nothing,
52+
weight_function=edge_data -> 1.0,
53+
default_weight=1.0,
54+
)
55+
56+
Construct a non-empty `MetaGraph` based on lists of vertices and edges with their labels and data.
57+
58+
These lists must be constructed as follows:
59+
- `vertices_description` is a vector of pairs `label => data` (the code of a vertex will correspond to its rank in the list)
60+
- `edges_description` is a vector of pairs `(label1, label2) => data`
61+
62+
Furthermore, they must be coherent with the `graph` argument, i.e. describe the same set of vertices and edges.
63+
"""
64+
function MetaGraph(
65+
graph::AbstractGraph{Code},
66+
vertices_description::Vector{Pair{Label,VertexData}},
67+
edges_description::Vector{Pair{Tuple{Label,Label},EdgeData}},
68+
graph_data=nothing,
69+
weight_function=edge_data -> 1.0,
70+
default_weight=1.0,
71+
) where {Code,Label,VertexData,EdgeData}
72+
# Construct vertex data
73+
@assert length(vertices_description) == nv(graph)
74+
vertex_labels = Dict{Code,Label}()
75+
vertex_properties = Dict{Label,Tuple{Code,VertexData}}()
76+
for (code, (label, data)) in enumerate(vertices_description)
77+
vertex_labels[code] = label
78+
vertex_properties[label] = (code, data)
79+
end
80+
# Construct edge data
81+
@assert length(edges_description) == ne(graph)
82+
for ((label_1, label_2), _) in edges_description
83+
code_1 = vertex_properties[label_1][1]
84+
code_2 = vertex_properties[label_2][1]
85+
@assert has_edge(graph, code_1, code_2)
86+
end
87+
edge_data = Dict{Tuple{Label,Label},EdgeData}()
88+
for ((label_1, label_2), data) in edges_description
89+
edge_data[label_1, label_2] = data
90+
end
91+
return MetaGraph(
92+
graph,
93+
vertex_labels,
94+
vertex_properties,
95+
edge_data,
96+
graph_data,
97+
weight_function,
98+
default_weight,
99+
)
100+
end
101+
39102
"""
40103
MetaGraph(
41104
graph;
42-
Label = Symbol,
43-
VertexData = Nothing,
44-
EdgeData = Nothing,
45-
graph_data = nothing,
46-
weight_function = edge_data -> 1.0,
47-
default_weight = 1.0
105+
Label=Symbol,
106+
VertexData=Nothing,
107+
EdgeData=Nothing,
108+
graph_data=nothing,
109+
weight_function=edge_data -> 1.0,
110+
default_weight=1.0
48111
)
49112
50113
Construct an empty `MetaGraph` with the given metadata types and weights.
114+
115+
!!! danger "Warning"
116+
This constructor is not type-stable, it is only there for convenience.
51117
"""
52118
function MetaGraph(
53119
graph::AbstractGraph{Code};
54120
Label=Symbol,
55121
VertexData=Nothing,
56122
EdgeData=Nothing,
57123
graph_data=nothing,
58-
weight_function=edata -> 1.0,
124+
weight_function=edge_data -> 1.0,
59125
default_weight=1.0,
60126
) where {Code}
127+
@assert nv(graph) == 0
61128
if Label <: Integer
62129
@warn "Constructing a MetaGraph with integer labels is not advised."
63-
elseif nv(graph) > 0
64-
@warn "Constructing a MetaGraph with a nonempty underlying graph is not advised."
65130
end
131+
vertex_labels = Dict{Code,Label}()
132+
vertex_properties = Dict{Label,Tuple{Code,VertexData}}()
133+
edge_data = Dict{Tuple{Label,Label},EdgeData}()
66134
return MetaGraph(
67135
graph,
68-
Dict{Code,Label}(),
69-
Dict{Label,Tuple{Code,VertexData}}(),
70-
Dict{Tuple{Label,Label},EdgeData}(),
136+
vertex_labels,
137+
vertex_properties,
138+
edge_data,
71139
graph_data,
72140
weight_function,
73141
default_weight,
74142
)
75143
end
76144

77-
function Base.zero(
78-
meta_graph::MetaGraph{Code,Label,Graph,VertexData,EdgeData}
79-
) where {Code,Label,Graph,VertexData,EdgeData}
80-
return MetaGraph(
81-
Graph();
82-
Label=Label,
83-
VertexData=VertexData,
84-
EdgeData=EdgeData,
85-
graph_data=meta_graph.graph_data,
86-
weight_function=meta_graph.weight_function,
87-
default_weight=meta_graph.default_weight,
88-
)
89-
end
90-
91-
function Base.:(==)(meta_graph_1::MetaGraph, meta_graph_2::MetaGraph)
92-
return meta_graph_1.graph == meta_graph_2.graph
93-
end
94-
95-
function Base.copy(meta_graph::MetaGraph)
96-
return deepcopy(meta_graph)
97-
end
98-
99145
function Base.show(
100-
io::IO, meta_graph::MetaGraph{<:Any,Label,<:Any,VertexData,EdgeData}
146+
io::IO, meta_graph::MetaGraph{<:Any,<:Any,Label,VertexData,EdgeData}
101147
) where {Label,VertexData,EdgeData}
102148
print(
103149
io,

test/tutorial/2_graphs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ has_edge(cities, 2, 3)
8181
# From this initial graph, we can create some others:
8282

8383
copy(cities)
84-
@test copy(cities) == cities #src
84+
@test copy(cities).graph == cities.graph #src
8585
#-
8686
zero(cities)
8787
@test nv(zero(cities)) == 0 #src

test/tutorial/3_files.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ example2 = mktemp() do file, io
1616
loadgraph(file, "something", MGFormat())
1717
end
1818

19-
example2 == example
20-
@test example2 == example #src
19+
example2.graph == example.graph
20+
@test example2.graph == example.graph #src
2121

2222
# ## DOTFormat
2323

0 commit comments

Comments
 (0)