Skip to content

Commit b14cae3

Browse files
committed
Add docs page dedicated to type stability and comparison with MetaGraphs.lj
1 parent 3469e7d commit b14cae3

File tree

6 files changed

+146
-44
lines changed

6 files changed

+146
-44
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ julia = "1.6"
1717
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
1818
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1919
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
20+
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
2021
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2122

2223
[targets]
23-
test = ["Aqua", "Documenter", "JuliaFormatter", "Test"]
24+
test = ["Aqua", "Documenter", "JuliaFormatter", "MetaGraphs", "Test"]

docs/Manifest.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
julia_version = "1.8.5"
44
manifest_format = "2.0"
5-
project_hash = "f82c2777847a88fb4979a0b1e71456c8301bca98"
5+
project_hash = "580b7ffaff126fd1040a2c45d9e0166b2dd82fd9"
66

77
[[deps.ANSIColoredPrinters]]
88
git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c"
@@ -159,6 +159,12 @@ deps = ["Artifacts", "Libdl"]
159159
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
160160
version = "2.28.0+0"
161161

162+
[[deps.MetaGraphs]]
163+
deps = ["Graphs", "JLD2", "Random"]
164+
git-tree-sha1 = "1130dbe1d5276cb656f6e1094ce97466ed700e5a"
165+
uuid = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
166+
version = "0.7.2"
167+
162168
[[deps.MetaGraphsNext]]
163169
deps = ["Graphs", "JLD2", "SimpleTraits"]
164170
path = ".."

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
33
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
44
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
5+
MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5"
56
MetaGraphsNext = "fa8bd995-216d-47f1-8a91-f3b68fbeb377"

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,8 @@ using Test
2626
@testset verbose = true "Files" begin
2727
include(joinpath("tutorial", "3_files.jl"))
2828
end
29+
@testset verbose = true "Type stability" begin
30+
include(joinpath("tutorial", "4_type_stability.jl"))
31+
end
2932
end
3033
end

test/tutorial/1_basics.jl

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ using Graphs
44
using MetaGraphsNext
55
using Test #src
66

7-
# ## Creating a `MetaGraph`
8-
9-
# ### Easiest constructor
7+
# ## Creating an empty `MetaGraph`
108

119
# We provide a convenience constructor for creating empty graphs, which looks as follows:
1210

@@ -20,36 +18,6 @@ colors = MetaGraph(
2018

2119
# The `label_type` argument defines how vertices will be referred to, it can be anything but an integer type (to avoid confusion with codes, see below). The `vertex_data_type` and `edge_data_type` type determine what kind of data will be associated with each vertex and edge. Finally, `graph_data` can contain an arbitrary object associated with the graph as a whole.
2220

23-
# ### Type stability
24-
25-
# However, since this constructor receives types as keyword arguments, it is type-unstable. Casual users may not care, but if your goal is performance, you might need one of the following alternatives: either wrap the constructor in a function...
26-
27-
function colors_constructor()
28-
return MetaGraph(
29-
Graph();
30-
label_type=Symbol,
31-
vertex_data_type=NTuple{3,Int},
32-
edge_data_type=Symbol,
33-
graph_data="additive colors",
34-
)
35-
end
36-
37-
colors_constructor()
38-
39-
@test @inferred colors_constructor() == colors #src
40-
41-
# ... or switch to positional arguments (be careful with the order!)
42-
43-
MetaGraph(Graph(), Symbol, NTuple{3,Int}, Symbol, "additive colors")
44-
45-
@test (@inferred MetaGraph( #src
46-
Graph(), #src
47-
Symbol, #src
48-
NTuple{3,Int}, #src
49-
Symbol, #src
50-
"additive colors", #src
51-
) == colors) #src
52-
5321
# ## Modifying the graph
5422

5523
# Modifications of graph elements and the associated metadata can always be done using `setindex!` (as in a dictionary) with the relevant labels.
@@ -70,9 +38,9 @@ colors[:red, :green] = :yellow;
7038
colors[:red, :blue] = :magenta;
7139
colors[:green, :blue] = :cyan;
7240

73-
# ### Creating a non-empty graph
41+
# ## Creating a non-empty `MetaGraph`
7442

75-
# There is a final constructor we haven't mentioned, which allows you to build and fill the `MetaGraph` in one fell swoop. Here's how it works:
43+
# There is an alternative constructor which allows you to build and fill the graph in one fell swoop. Here's how it works:
7644

7745
graph = Graph(Edge.([(1, 2), (1, 3), (2, 3)]))
7846
vertices_description = [:red => (255, 0, 0), :green => (0, 255, 0), :blue => (0, 0, 255)]
@@ -83,13 +51,6 @@ edges_description = [
8351
colors2 = MetaGraph(graph, vertices_description, edges_description, "additive colors")
8452
colors2 == colors
8553

86-
@test (@inferred MetaGraph( #src
87-
graph, #src
88-
vertices_description, #src
89-
edges_description, #src
90-
"additive colors", #src
91-
) == colors) #src
92-
9354
# ## Accessing graph properties
9455

9556
# To retrieve graph properties, we still follow a dictionary-like interface based on labels.

test/tutorial/4_type_stability.jl

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# # Type stability
2+
3+
using Graphs
4+
using MetaGraphs: MetaGraphs
5+
using MetaGraphsNext
6+
using Test #src
7+
8+
# ## Constructor and access
9+
10+
# In the previous examples, we used a `MetaGraph` constructor which receives type parameters as keyword arguments. This was done for ease of exposition, but it may impede type inference, and hence reduce performance.
11+
12+
colors = MetaGraph(
13+
Graph(); # underlying graph structure
14+
label_type=Symbol, # color name
15+
vertex_data_type=NTuple{3,Int}, # RGB code
16+
edge_data_type=Symbol, # result of the addition between two colors
17+
graph_data="additive colors", # tag for the whole graph
18+
)
19+
20+
@test_throws ErrorException (@inferred MetaGraph( #src
21+
Graph(); #src
22+
label_type=Symbol, #src
23+
vertex_data_type=NTuple{3,Int}, #src
24+
edge_data_type=Symbol, #src
25+
graph_data="additive colors", #src
26+
)) #src
27+
28+
# While casual users probably won't care, if your goal is performance, you might need to proceed differently.
29+
30+
# Option 1: wrap the constructor in a helper function to trigger constant propagation.
31+
32+
function colors_constructor()
33+
return MetaGraph(
34+
Graph();
35+
label_type=Symbol,
36+
vertex_data_type=NTuple{3,Int},
37+
edge_data_type=Symbol,
38+
graph_data="additive colors",
39+
)
40+
end
41+
42+
colors_constructor()
43+
44+
@test @inferred colors_constructor() == colors #src
45+
46+
# Option 2: switch to another constructor that uses positional arguments (be careful with the order!)
47+
48+
MetaGraph(Graph(), Symbol, NTuple{3,Int}, Symbol, "additive colors")
49+
50+
@test (@inferred MetaGraph( #src
51+
Graph(), #src
52+
Symbol, #src
53+
NTuple{3,Int}, #src
54+
Symbol, #src
55+
"additive colors", #src
56+
) == colors) #src
57+
58+
# Option 3: use the constructor for a non-empty graph instead.
59+
60+
vertices_description = [:red => (255, 0, 0), :green => (0, 255, 0), :blue => (0, 0, 255)]
61+
edges_description = [
62+
(:red, :green) => :yellow, (:red, :blue) => :magenta, (:green, :blue) => :cyan
63+
]
64+
MetaGraph(cycle_graph(3), vertices_description, edges_description, "additive colors")
65+
66+
@test (@inferred zero(
67+
MetaGraph( #src
68+
cycle_graph(3), #src
69+
vertices_description, #src
70+
edges_description, #src
71+
"additive colors", #src
72+
)
73+
) == colors) #src
74+
75+
# Once Julia can infer the full type of the `MetaGraph`, accessing vertex and edge metadata also becomes type-stable.
76+
77+
# ## Comparison with MetaGraphs.jl
78+
79+
# In the older package [MetaGraphs.jl](https://github.com/JuliaGraphs/MetaGraphs.jl) that we used as inspiration, data types are not specified in the graph structure. Their choice allows more flexibility and an arbitrary number of attributes which the user does not need to anticipate at construction.
80+
81+
colors_unstable = MetaGraphs.MetaGraph(cycle_graph(3))
82+
83+
# Here is how one would add data and labels to `colors_unstable`.
84+
85+
MetaGraphs.set_indexing_prop!(colors_unstable, :label)
86+
87+
MetaGraphs.set_prop!(colors_unstable, :graph_tag, "additive colors")
88+
89+
MetaGraphs.set_props!(colors_unstable, 1, Dict(:label => :red, :rgb_code => (255, 0, 0)))
90+
MetaGraphs.set_props!(colors_unstable, 2, Dict(:label => :green, :rgb_code => (0, 255, 0)))
91+
MetaGraphs.set_props!(colors_unstable, 3, Dict(:label => :blue, :rgb_code => (0, 0, 255)))
92+
93+
MetaGraphs.set_prop!(colors_unstable, 1, 2, :addition_result, :yellow)
94+
MetaGraphs.set_prop!(colors_unstable, 1, 3, :addition_result, :magenta)
95+
MetaGraphs.set_prop!(colors_unstable, 2, 3, :addition_result, :cyan);
96+
97+
# One can retrieve the vertex index (which we called code) using any indexing property.
98+
99+
colors_unstable[:green, :label]
100+
101+
# Then we can access vertex properties...
102+
103+
MetaGraphs.get_prop(colors_unstable, 2, :rgb_code)
104+
105+
@test_throws ErrorException (@inferred MetaGraphs.get_prop( #src
106+
colors_unstable, #src
107+
2, #src
108+
:rgb_code, #src
109+
)) #src
110+
111+
#-
112+
113+
MetaGraphs.props(colors_unstable, 2)
114+
115+
# ... and edge properties.
116+
117+
MetaGraphs.get_prop(colors_unstable, 2, 3, :addition_result)
118+
119+
@test_throws ErrorException (@inferred MetaGraphs.get_prop( #src
120+
colors_unstable, #src
121+
2, #src
122+
3, #src
123+
:addition_result, #src
124+
)) #src
125+
126+
#-
127+
128+
MetaGraphs.props(colors_unstable, 2, 3)
129+
130+
# The fact that the outputs of these calls to `props` are of type `Dict{Symbol, Any}` is at the root of the problem. It means that if we use their values in any subsequent algorithms, we introduce type instability in our code (due to `Any`). MetaGraphsNext.jl overcomes this obstacle thanks to a more precise storage method.

0 commit comments

Comments
 (0)