Skip to content
Closed
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## Unreleased

- Complete `Graphs.jl` `AbstractGraph` interface for `VNGraph`: `edges`, `outneighbors`/`inneighbors`, `rem_edge!`, `add_vertex!`, `copy`.
- Fix 0-indexing bug in `has_edge` (was passing 1-based indices to 0-based C API).
- Add `GraphsInterfaceChecker.jl` compliance tests.
- Add `VNAlgorithm` dispatch for `is_connected`, `connected_components`, `chromatic_number`, `edge_chromatic_number`, and `clique_number`.
178 changes: 169 additions & 9 deletions src/VNGraphs.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module VNGraphs

export VNGraph
export VNGraph, VNAlgorithm, chromatic_number, edge_chromatic_number, clique_number

import Graphs

Expand Down Expand Up @@ -105,17 +105,177 @@ end

Base.eltype(::VNGraph) = Cuint
Base.zero(::Type{VNGraph}) = VNGraph(0)
# Graphs.edges # TODO
Graphs.edgetype(g::VNGraph) = Graphs.SimpleGraphs.SimpleEdge{eltype(g)}
Graphs.has_edge(g::VNGraph,s,d) = graph_has_edge(g,s,d)
Graphs.has_vertex(g::VNGraph,n::Integer) = 1≤n≤nnodes(g)
# Graphs.inneighbors # TODO
Graphs.edgetype(::VNGraph) = Graphs.SimpleGraphs.SimpleEdge{Cuint}
Graphs.is_directed(::Type{VNGraph}) = false
Graphs.ne(g::VNGraph) = nedges(g)
Graphs.nv(g::VNGraph) = nnodes(g)
# Graphs.outneighbors # TODO
Graphs.vertices(g::VNGraph) = 1:nnodes(g)
Graphs.vertices(g::VNGraph)::UnitRange{Cuint} = Cuint(1):Cuint(nnodes(g))

Graphs.add_edge!(g::VNGraph, e::Graphs.SimpleGraphEdge) = graph_add_edge(g,e.src-1,e.dst-1)
# Fix: convert 1-based Julia indices to 0-based C indices
Graphs.has_edge(g::VNGraph, s, d)::Bool = graph_has_edge(g, s-1, d-1)
Graphs.has_vertex(g::VNGraph, n::Integer) = 1 ≤ n ≤ nnodes(g)

function Graphs.outneighbors(g::VNGraph, v::Integer)
(1 ≤ v ≤ nnodes(g)) || return Cuint[]
deg = g.ptr.d[][v]
neighbors = Vector{Cuint}(undef, deg)
adj = g.ptr.a[][v]
for k in 1:deg
neighbors[k] = adj[k][] + Cuint(1) # 0-based C → 1-based Julia
end
sort!(neighbors)
return neighbors
end

Graphs.inneighbors(g::VNGraph, v::Integer) = Graphs.outneighbors(g, v)

"""Iterator over edges of a VNGraph, yielding SimpleEdge{Cuint}."""
struct VNGraphEdgeIterator
graph::VNGraph
end

Base.length(it::VNGraphEdgeIterator) = nedges(it.graph)
Base.eltype(::Type{VNGraphEdgeIterator}) = Graphs.SimpleGraphs.SimpleEdge{Cuint}

function Base.iterate(it::VNGraphEdgeIterator, state=(Cuint(1), 1))
g = it.graph
n = nnodes(g)
v, kidx = state
while v ≤ n
deg = g.ptr.d[][v]
adj = g.ptr.a[][v]
while kidx ≤ deg
w = adj[kidx][] + Cuint(1)
kidx += 1
if v < w # undirected: emit each edge once (lower index first)
return (Graphs.SimpleGraphs.SimpleEdge{Cuint}(v, w), (v, kidx))
end
end
v += Cuint(1)
kidx = 1
end
return nothing
end

Graphs.edges(g::VNGraph) = VNGraphEdgeIterator(g)

function Graphs.add_edge!(g::VNGraph, e::Graphs.SimpleGraphEdge)
s, d = e.src - 1, e.dst - 1
graph_add_edge(g, s, d)
return Graphs.has_edge(g, e.src, e.dst)
end

function Graphs.rem_edge!(g::VNGraph, e::Graphs.SimpleGraphEdge)
s, d = e.src - 1, e.dst - 1
result = graph_del_edge(g, s, d)
return result != 0
end

function Graphs.add_vertex!(g::VNGraph)
graph_add_node(g)
return true
end

function Base.copy(g::VNGraph)
n = nnodes(g)
g2 = VNGraph(n)
for i in Cuint(1):Cuint(n)
deg = g.ptr.d[][i]
adj = g.ptr.a[][i]
for k in 1:deg
j = adj[k][] + Cuint(1)
if i < j
graph_add_edge(g2, i - 1, j - 1)
end
end
end
return g2
end

# --- VNAlgorithm dispatch ---

"""
VNAlgorithm

Algorithm dispatch type for very_nauty C library implementations.
Pass `VNAlgorithm()` as a second argument to dispatch graph algorithms
to the very_nauty C implementation. Non-VNGraph inputs are automatically
converted.

# Example
```julia
g = path_graph(5)
is_connected(g, VNAlgorithm()) # uses very_nauty C implementation
chromatic_number(g, VNAlgorithm()) # exact chromatic number via very_nauty
```
"""
struct VNAlgorithm end

# Helper: convert to VNGraph if needed
_to_vngraph(g::VNGraph) = g
_to_vngraph(g::Graphs.AbstractSimpleGraph) = VNGraph(g)

"""
Graphs.is_connected(g::AbstractGraph, ::VNAlgorithm)

Test whether `g` is connected using the very_nauty C library.
"""
function Graphs.is_connected(g::Graphs.AbstractGraph, ::VNAlgorithm)
vng = _to_vngraph(g)
return graph_connected(vng) != 0
end

"""
Graphs.connected_components(g::AbstractGraph, ::VNAlgorithm)

Return the connected components of `g` using the very_nauty C library.
Returns a vector of vectors, where each inner vector contains the 1-based
vertex indices of one component.
"""
function Graphs.connected_components(g::Graphs.AbstractGraph, ::VNAlgorithm)
vng = _to_vngraph(g)
nc = graph_nclusters(vng)
n = nnodes(vng)
components = [Int[] for _ in 1:nc]
for i in Cuint(1):Cuint(n)
label = cluster(vng, i) + 1 # 0-based C label → 1-based
push!(components[label], Int(i))
end
return components
end

"""
chromatic_number(g::AbstractGraph, ::VNAlgorithm; timeout=0)

Compute the exact chromatic number of `g` using the very_nauty C library.
`timeout` specifies CPU clock ticks before giving up (0 = no timeout).
"""
function chromatic_number(g::Graphs.AbstractGraph, ::VNAlgorithm; timeout=0)
vng = _to_vngraph(g)
return Int(graph_chromatic_number(vng, timeout))
end

"""
edge_chromatic_number(g::AbstractGraph, ::VNAlgorithm; timeout=0)

Compute the exact edge chromatic number (chromatic index) of `g`
using the very_nauty C library.
`timeout` specifies CPU clock ticks before giving up (0 = no timeout).
"""
function edge_chromatic_number(g::Graphs.AbstractGraph, ::VNAlgorithm; timeout=0)
vng = _to_vngraph(g)
return Int(graph_edge_chromatic_number(vng, timeout))
end

"""
clique_number(g::AbstractGraph, ::VNAlgorithm)

Compute the exact clique number (size of the maximum clique) of `g`
using the very_nauty C library.
"""
function clique_number(g::Graphs.AbstractGraph, ::VNAlgorithm)
vng = _to_vngraph(g)
return Int(graph_clique_number(vng))
end

end
2 changes: 2 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
GraphsInterfaceChecker = "3bef136c-15ff-4091-acbb-1a4aafe67608"
Interfaces = "85a1e053-f937-4924-92a5-1367d23b7b87"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
Loading
Loading