Skip to content

Commit 78818a0

Browse files
simonschoellyKrastanovclaude
authored
Add FrozenVector to wrap neighbors (#317)
* Add FrozenVector to wrap neighbors * Apply JuliaFormatter (BlueStyle, v2.2.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix ReverseView doctest to match FrozenVector output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add FrozenVector breaking change to CHANGELOG Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add doctest filter for FrozenVector empty array printing Julia 1.13 changed how empty AbstractVector subtypes are displayed. Previously (<=1.12), an empty FrozenVector printed as: 0-element Graphs.FrozenVector{Int64} In Julia 1.13+, it prints as: Int64[] This broke the doctests for `neighbors` and `ReverseView` that show empty FrozenVector output. The fix adds a local doctest filter to each affected jldoctest block that normalizes both representations before comparison. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Stefan Krastanov <github.acc@krastanov.org> Co-authored-by: Stefan Krastanov <stefan@krastanov.org> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2bc509d commit 78818a0

File tree

7 files changed

+51
-19
lines changed

7 files changed

+51
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# News
22

33
## dev - unreleased
4+
- **(breaking)** `neighbors`, `inneighbors`, and `outneighbors` now return an immutable `FrozenVector` instead of `Vector`
45
- Louvain community detection algorithm
56
- Graph views: `ReverseView` and `UndirectedView` for directed graphs
67
- New graph products: `strong_product`, `disjunctive_product`, `lexicographic_product`, `homomorphic_product`

src/Graphs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ and tutorials are available at the
478478
Graphs
479479
include("interface.jl")
480480
include("utils.jl")
481+
include("frozenvector.jl")
481482
include("deprecations.jl")
482483
include("core.jl")
483484

src/SimpleGraphs/SimpleGraphs.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Graphs:
1515
AbstractGraph,
1616
AbstractEdge,
1717
AbstractEdgeIter,
18+
FrozenVector,
1819
src,
1920
dst,
2021
edgetype,
@@ -153,8 +154,8 @@ add_edge!(g::AbstractSimpleGraph, x) = add_edge!(g, edgetype(g)(x))
153154
has_edge(g::AbstractSimpleGraph, x, y) = has_edge(g, edgetype(g)(x, y))
154155
add_edge!(g::AbstractSimpleGraph, x, y) = add_edge!(g, edgetype(g)(x, y))
155156

156-
inneighbors(g::AbstractSimpleGraph, v::Integer) = badj(g, v)
157-
outneighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g, v)
157+
inneighbors(g::AbstractSimpleGraph, v::Integer) = FrozenVector(badj(g, v))
158+
outneighbors(g::AbstractSimpleGraph, v::Integer) = FrozenVector(fadj(g, v))
158159

159160
function issubset(g::T, h::T) where {T<:AbstractSimpleGraph}
160161
nv(g) <= nv(h) || return false

src/core.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,13 @@ For directed graphs, the default is equivalent to [`outneighbors`](@ref);
248248
use [`all_neighbors`](@ref) to list inbound and outbound neighbors.
249249
250250
### Implementation Notes
251-
Returns a reference to the current graph's internal structures, not a copy.
252-
Do not modify result. If the graph is modified, the behavior is undefined:
251+
In some cases might return a reference to the current graph's internal structures,
252+
not a copy. Do not modify result. If the graph is modified, the behavior is undefined:
253253
the array behind this reference may be modified too, but this is not guaranteed.
254+
If you need to modify the result use `collect` or `copy` to create a copy.
254255
255256
# Examples
256-
```jldoctest
257+
```jldoctest; filter = r"0-element Graphs\\.FrozenVector\\{Int64\\}|Int64\\[\\]"
257258
julia> using Graphs
258259
259260
julia> g = DiGraph(3);
@@ -263,14 +264,14 @@ julia> add_edge!(g, 2, 3);
263264
julia> add_edge!(g, 3, 1);
264265
265266
julia> neighbors(g, 1)
266-
Int64[]
267+
0-element Graphs.FrozenVector{Int64}
267268
268269
julia> neighbors(g, 2)
269-
1-element Vector{Int64}:
270+
1-element Graphs.FrozenVector{Int64}:
270271
3
271272
272273
julia> neighbors(g, 3)
273-
1-element Vector{Int64}:
274+
1-element Graphs.FrozenVector{Int64}:
274275
1
275276
```
276277
"""
@@ -284,9 +285,10 @@ For undirected graphs, this is equivalent to both [`outneighbors`](@ref)
284285
and [`inneighbors`](@ref).
285286
286287
### Implementation Notes
287-
Returns a reference to the current graph's internal structures, not a copy.
288-
Do not modify result. If the graph is modified, the behavior is undefined:
288+
In some cases might return a reference to the current graph's internal structures,
289+
not a copy. Do not modify result. If the graph is modified, the behavior is undefined:
289290
the array behind this reference may be modified too, but this is not guaranteed.
291+
If you need to modify the result use `collect` or `copy` to create a copy.
290292
291293
# Examples
292294
```jldoctest

src/frozenvector.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
FrozenVector(v::Vector) <: AbstractVector
3+
4+
A data structure that wraps a `Vector` but does not allow modifications.
5+
"""
6+
struct FrozenVector{T} <: AbstractVector{T}
7+
wrapped::Vector{T}
8+
end
9+
10+
Base.size(v::FrozenVector) = Base.size(v.wrapped)
11+
12+
Base.@propagate_inbounds Base.getindex(v::FrozenVector, i::Int) = Base.getindex(
13+
v.wrapped, i
14+
)
15+
16+
Base.IndexStyle(v::Type{FrozenVector{T}}) where {T} = Base.IndexStyle(Vector{T})
17+
18+
Base.iterate(v::FrozenVector) = Base.iterate(v.wrapped)
19+
Base.iterate(v::FrozenVector, state) = Base.iterate(v.wrapped, state)
20+
21+
Base.similar(v::FrozenVector) = Base.similar(v.wrapped)
22+
Base.similar(v::FrozenVector, T::Type) = Base.similar(v.wrapped, T)
23+
Base.similar(v::FrozenVector, T::Type, dims::Base.Dims) = Base.similar(v.wrapped, T, dims)
24+
25+
Base.copy(v::FrozenVector) = Base.copy(v.wrapped)

src/interface.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,10 @@ has_edge(g, e) = has_edge(g, src(e), dst(e))
281281
Return a list of all neighbors connected to vertex `v` by an incoming edge.
282282
283283
### Implementation Notes
284-
Returns a reference to the current graph's internal structures, not a copy.
285-
Do not modify result. If the graph is modified, the behavior is undefined:
284+
In some cases might return a reference to the current graph's internal structures,
285+
not a copy. Do not modify result. If the graph is modified, the behavior is undefined:
286286
the array behind this reference may be modified too, but this is not guaranteed.
287+
If you need to modify the result use `collect` or `copy` to create a copy.
287288
288289
# Examples
289290
```jldoctest
@@ -292,7 +293,7 @@ julia> using Graphs
292293
julia> g = SimpleDiGraph([0 1 0 0 0; 0 0 1 0 0; 1 0 0 1 0; 0 0 0 0 1; 0 0 0 1 0]);
293294
294295
julia> inneighbors(g, 4)
295-
2-element Vector{Int64}:
296+
2-element Graphs.FrozenVector{Int64}:
296297
3
297298
5
298299
```
@@ -305,9 +306,10 @@ inneighbors(x, v) = _NI("inneighbors")
305306
Return a list of all neighbors connected to vertex `v` by an outgoing edge.
306307
307308
# Implementation Notes
308-
Returns a reference to the current graph's internal structures, not a copy.
309-
Do not modify result. If the graph is modified, the behavior is undefined:
309+
In some cases might return a reference to the current graph's internal structures,
310+
not a copy. Do not modify result. If the graph is modified, the behavior is undefined:
310311
the array behind this reference may be modified too, but this is not guaranteed.
312+
If you need to modify the result use `collect` or `copy` to create a copy.
311313
312314
# Examples
313315
```jldoctest
@@ -316,7 +318,7 @@ julia> using Graphs
316318
julia> g = SimpleDiGraph([0 1 0 0 0; 0 0 1 0 0; 1 0 0 1 0; 0 0 0 0 1; 0 0 0 1 0]);
317319
318320
julia> outneighbors(g, 4)
319-
1-element Vector{Int64}:
321+
1-element Graphs.FrozenVector{Int64}:
320322
5
321323
```
322324
"""

src/wrappedGraphs/graphviews.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A graph view that wraps a directed graph and reverse the direction of every edge
99
constructing the view may lead to incorrect results.
1010
1111
# Examples
12-
```jldoctest
12+
```jldoctest; filter = r"0-element Graphs\\.FrozenVector\\{Int64\\}|Int64\\[\\]"
1313
julia> using Graphs
1414
1515
julia> g = SimpleDiGraph(2);
@@ -19,10 +19,10 @@ julia> add_edge!(g, 1, 2);
1919
julia> rg = ReverseView(g);
2020
2121
julia> neighbors(rg, 1)
22-
Int64[]
22+
0-element Graphs.FrozenVector{Int64}
2323
2424
julia> neighbors(rg, 2)
25-
1-element Vector{Int64}:
25+
1-element Graphs.FrozenVector{Int64}:
2626
1
2727
```
2828
"""

0 commit comments

Comments
 (0)