Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

We follow SemVer as most of the Julia ecosystem. Below you might see the "breaking" label even for minor version bumps -- we use it a bit more loosely to denote things that are not breaking by SemVer's definition but might cause breakage to people using internal or experimental APIs or undocumented implementation details.

## Unreleased
- `has_negative_edge_cycle_spfa` now detects negative cycles unreachable from vertex 1

## v1.15.0 - 2026-09-07
- `is_articulation(g, v)` for checking whether a single vertex is an articulation point
- The iFUB algorithm is used for faster diameter calculation and now supports weighted graph diameter calculation
Expand Down
16 changes: 11 additions & 5 deletions src/shortestpaths/spfa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,21 @@ function spfa_shortest_paths(

(source in 1:nvg) ||
throw(DomainError(source, "source should be in between 1 and $nvg"))
return _spfa_shortest_paths(graph, [source], distmx)
end

function _spfa_shortest_paths(
graph::AbstractGraph{U}, sources::AbstractVector{<:Integer}, distmx::AbstractMatrix{T}
) where {T<:Number} where {U<:Integer}
nvg = nv(graph)
dists = fill(typemax(T), nvg)
dists[source] = 0
dists[sources] .= zero(T)

count = zeros(U, nvg) # Vector to store the count of number of times a vertex goes in the queue.

queue = Vector{U}() # Vector used to implement queue
queue = Vector{U}(sources) # Vector used to implement queue
inqueue = falses(nvg, 1) # BitArray to mark which vertices are in queue
push!(queue, source)
inqueue[source] = true
inqueue[sources] .= true

@inbounds while !isempty(queue)
v = popfirst!(queue)
Expand Down Expand Up @@ -117,7 +123,7 @@ function has_negative_edge_cycle_spfa(
g::AbstractGraph{U}, distmx::AbstractMatrix{T}
) where {T<:Number} where {U<:Integer}
try
spfa_shortest_paths(g, 1, distmx)
_spfa_shortest_paths(g, collect_if_not_vector(vertices(g)), distmx)
catch e
isa(e, NegativeCycleError) && return true
end
Expand Down
10 changes: 10 additions & 0 deletions test/shortestpaths/spfa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@
end

@testset "Negative Cycle" begin
@testset "Negative cycle unreachable from vertex 1" begin
gx = SimpleDiGraph(3)
add_edge!(gx, 2, 3)
add_edge!(gx, 3, 2)
d = [0 0 0; 0 0 -2; 0 1 0]
for g in test_generic_graphs(gx)
@test has_negative_edge_cycle_spfa(g, d)
end
end

# Negative Cycle 1
gx = complete_graph(3)
for g in test_generic_graphs(gx)
Expand Down
Loading