diff --git a/Project.toml b/Project.toml index 5d2f078..8d0a3ed 100644 --- a/Project.toml +++ b/Project.toml @@ -10,8 +10,8 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] -ColPack_jll = "0.3.0" -LinearAlgebra = "1" -Random = "1" -SparseArrays = "1" +ColPack_jll = "0.4.0" +LinearAlgebra = "1.6" +Random = "1.6" +SparseArrays = "1.6" julia = "1.6" diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md index 947d69a..a809da7 100644 --- a/docs/src/tutorial.md +++ b/docs/src/tutorial.md @@ -27,7 +27,7 @@ julia> adjJ = ColPack.matrix2adjmatrix(J; partition_by_rows=false) julia> coloring = ColPackColoring(adjJ, d1_coloring(), natural_ordering()); julia> colors = get_colors(coloring) -5-element Vector{Int64}: +5-element Vector{Int32}: 1 2 1 diff --git a/examples/Project.toml b/examples/Project.toml new file mode 100644 index 0000000..4c69167 --- /dev/null +++ b/examples/Project.toml @@ -0,0 +1,8 @@ +[deps] +ColPack = "ffa27691-3a59-46ab-a8d4-551f45b8d401" +ColPack_jll = "f218ff0c-cb54-5151-80c4-c0f62c730ce6" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MatrixMarket = "4d4711f2-db25-561a-b6b3-d35e7d4047d3" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" +SuiteSparseMatrixCollection = "ac199af8-68bc-55b8-82c4-7abd6f96ed98" diff --git a/examples/coloring.jl b/examples/coloring.jl new file mode 100644 index 0000000..b5a4872 --- /dev/null +++ b/examples/coloring.jl @@ -0,0 +1,64 @@ +using ColPack +using SuiteSparseMatrixCollection +using MatrixMarket +using SparseArrays +using SparseDiffTools +using LinearAlgebra + +ssmc = ssmc_db() +matrices = ssmc_matrices(ssmc, "Bodendiek", "CurlCurl_0") +folders = fetch_ssmc(matrices, format="MM") +paths = joinpath.(folders, matrices.name .* ".mtx") +A = MatrixMarket.mmread(paths[1]) + +orderings = Vector{ColoringOrder}() +push!(orderings, natural_ordering()) +push!(orderings, largest_first_ordering()) +push!(orderings, dynamic_largest_first_ordering()) +push!(orderings, distance_two_largest_first_ordering()) +push!(orderings, smallest_last_ordering()) +push!(orderings, distance_two_smallest_last_ordering()) +push!(orderings, incidence_degree_ordering()) +push!(orderings, distance_two_incidence_degree_ordering()) +push!(orderings, random_ordering()) + +@time sparsediff_coloring = SparseDiffTools.matrix_colors(A) +ncolors_sdt = maximum(sparsediff_coloring) + +method = ColPack.d1_coloring() +verbose = false +@time colpack_coloring = ColPackColoring(paths[1], method, random_ordering(); verbose) + +for ordering in orderings + colpack_coloring = ColPackColoring(paths[1], method, ordering; verbose) +end + +colors_colpack = get_colors(colpack_coloring) +ncolors_colpack = maximum(colors_colpack) + +method = row_partial_d2_coloring() +@time colpack_partial_coloring1 = ColPackPartialColoring(paths[1], method, natural_ordering(); verbose) +colors_row = get_colors(colpack_partial_coloring1) +maximum(colors_row) + +method = column_partial_d2_coloring() +@time colpack_partial_coloring2 = ColPackPartialColoring(paths[1], method, natural_ordering(); verbose) +colors_column = get_colors(colpack_partial_coloring2) +maximum(colors_column) + +method = row_partial_d2_coloring() +@time colpack_partial_coloring1 = ColPackPartialColoring(A, method, natural_ordering(); verbose) +colors_row2 = get_colors(colpack_partial_coloring1) +maximum(colors_row2) + +method = column_partial_d2_coloring() +@time colpack_partial_coloring2 = ColPackPartialColoring(A, method, natural_ordering(); verbose) +colors_column2 = get_colors(colpack_partial_coloring2) +maximum(colors_column2) + +method = implicit_star_bicoloring() +# method = explicit_star_bicoloring() +# method = explicit_modified_star_bicoloring() +# method = implicit_greedy_star_bicoloring() +@time colpack_bicoloring = ColPackBiColoring(paths[1], method, random_ordering(); verbose) +colors1, colors2 = get_colors(colpack_bicoloring) diff --git a/src/ColPack.jl b/src/ColPack.jl index 2916372..03f120d 100644 --- a/src/ColPack.jl +++ b/src/ColPack.jl @@ -8,15 +8,20 @@ using SparseArrays # Definitions +include("libcolpack.jl") include("method.jl") include("order.jl") include("utils.jl") -include("colpackcoloring.jl") +include("colpack_coloring.jl") +include("colpack_partial_coloring.jl") +include("colpack_bicoloring.jl") # Exports export ColoringMethod export d1_coloring, d2_coloring, acyclic_coloring, star_coloring +export row_partial_d2_coloring, column_partial_d2_coloring +export implicit_star_bicoloring, explicit_star_bicoloring, explicit_modified_star_bicoloring, implicit_greedy_star_bicoloring export ColoringOrder export natural_ordering, largest_first_ordering, dynamic_largest_first_ordering, distance_two_largest_first_ordering @@ -25,6 +30,6 @@ export random_ordering export matrix2adjmatrix -export ColPackColoring, get_colors +export ColPackColoring, ColPackPartialColoring, ColPackBiColoring, get_colors end #module diff --git a/src/colpack_bicoloring.jl b/src/colpack_bicoloring.jl new file mode 100644 index 0000000..5f5335c --- /dev/null +++ b/src/colpack_bicoloring.jl @@ -0,0 +1,73 @@ +""" + ColPackBiColoring + +Struct holding the parameters of a bicoloring as well as its results (which can be queried with [`get_colors`](@ref)). + +# Fields + +The fields of this struct are not part of the public API, they are only useful to interface with the C++ library [ColPack](https://github.com/CSCsw/ColPack). + +# Constructors + + ColPackBiColoring( + filename::AbstractString, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false + ) + + ColPackBiColoring( + M::SparseMatrixCSC, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false + ) + +Perform the coloring of a matrix that is either given directly or read from a file. + +The users needs to specify a bicoloring `method` and an `order` on the vertices. + +# See also + +- [`ColoringMethod`](@ref) +- [`ColoringOrder`](@ref) +""" +mutable struct ColPackBiColoring + refColPack::Base.RefValue{Ptr{Cvoid}} + coloring1::Vector{Cint} + coloring2::Vector{Cint} + method::ColoringMethod + order::ColoringOrder +end + +Base.unsafe_convert(::Type{Ptr{Cvoid}}, coloring::ColPackBiColoring) = coloring.refColPack[] + +function ColPackBiColoring( + filename::AbstractString, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false, +) + refColPack = Ref{Ptr{Cvoid}}(C_NULL) + reflen1 = Ref{Cint}(0) + reflen2 = Ref{Cint}(0) + ret = build_bicoloring_from_file(refColPack, reflen1, reflen2, filename, method.method, order.order, verbose) + (ret == 0) && error("ColPack bicoloring failed.") + coloring1 = zeros(Cint, reflen1[]) + coloring2 = zeros(Cint, reflen2[]) + g = ColPackBiColoring(refColPack, coloring1, coloring2, method, order) + finalizer(free_bicoloring, g) + return g +end + +""" + get_colors(coloring::ColPackBiColoring) + +Retrieve the colors from a [`ColPackBiColoring`](@ref) as vectors of integers. +""" +function get_colors(coloring::ColPackBiColoring) + get_bicoloring(coloring.refColPack[], coloring.coloring1, coloring.coloring2) + coloring.coloring1 .+= Cint(1) + coloring.coloring2 .+= Cint(1) + return coloring.coloring1, coloring.coloring2 +end diff --git a/src/colpackcoloring.jl b/src/colpack_coloring.jl similarity index 56% rename from src/colpackcoloring.jl rename to src/colpack_coloring.jl index 068eca6..44e0841 100644 --- a/src/colpackcoloring.jl +++ b/src/colpack_coloring.jl @@ -37,13 +37,9 @@ mutable struct ColPackColoring coloring::Vector{Cint} method::ColoringMethod order::ColoringOrder - csr::Union{Vector{Ptr{Cuint}},Nothing} end -function free_coloring(g::ColPackColoring) - @ccall libcolpack.free_coloring(g.refColPack::Ptr{Cvoid})::Cvoid - return nothing -end +Base.unsafe_convert(::Type{Ptr{Cvoid}}, coloring::ColPackColoring) = coloring.refColPack[] function ColPackColoring( filename::AbstractString, @@ -51,32 +47,23 @@ function ColPackColoring( order::ColoringOrder; verbose::Bool=false, ) - reflen = Vector{Cint}([Cint(0)]) refColPack = Ref{Ptr{Cvoid}}(C_NULL) - ret = @ccall libcolpack.build_coloring( - refColPack::Ptr{Cvoid}, - reflen::Ptr{Cint}, - filename::Cstring, - method.method::Cstring, - order.order::Cstring, - verbose::Cint, - )::Cint - if ret == 0 - error("ColPack coloring failed.") - end - - g = ColPackColoring(refColPack, zeros(Int, reflen[]), method, order, nothing) + reflen = Ref{Cint}(0) + ret = build_coloring_from_file(refColPack, reflen, filename, method.method, order.order, verbose) + (ret == 0) && error("ColPack coloring failed.") + coloring = zeros(Cint, reflen[]) + g = ColPackColoring(refColPack, coloring, method, order) finalizer(free_coloring, g) return g end function ColPackColoring( - M::SparseMatrixCSC{VT,IT}, + M::SparseMatrixCSC, method::ColoringMethod, order::ColoringOrder; - verbose::Bool=false, -) where {VT,IT} - @assert issymmetric(M) + verbose::Bool=false +) + # We expect M to be symmetric. csr = Vector{Ref{Cuint}}() csr_mem = Vector{Vector{Cuint}}() for i in 1:(length(M.colptr) - 1) @@ -92,36 +79,22 @@ function ColPackColoring( nrows = size(M, 2) reflen = Ref{Cint}(0) refColPack = Ref{Ptr{Cvoid}}(C_NULL) - ret = @ccall libcolpack.build_coloring_from_csr( - refColPack::Ptr{Cvoid}, - reflen::Ptr{Cint}, - csr::Ref{Ptr{Cuint}}, - nrows::Cint, - method.method::Cstring, - order.order::Cstring, - verbose::Cint, - )::Cint - if ret == 0 - error("ColPack coloring failed.") - end + ret = build_coloring_from_adolc(refColPack, reflen, csr, nrows, method.method, order.order, verbose) + (ret == 0) && error("ColPack coloring failed.") - g = ColPackColoring(refColPack, zeros(Int, reflen[]), method, order, csr) + coloring = zeros(Cint, reflen[]) + g = ColPackColoring(refColPack, coloring, method, order) finalizer(free_coloring, g) return g end """ - get_colors(coloring::ColPackColoring; verbose=false) + get_colors(coloring::ColPackColoring) Retrieve the colors from a [`ColPackColoring`](@ref) as a vector of integers. """ -function get_colors(coloring::ColPackColoring; verbose=false) - @ccall libcolpack.get_colors( - coloring.refColPack[]::Ptr{Cvoid}, - coloring.coloring::Ptr{Cdouble}, # TODO: should this be Cint? - coloring.method.method::Cstring, - verbose::Cint, - )::Cvoid - # Julia colorings should be base 1 - return coloring.coloring .+ 1 +function get_colors(coloring::ColPackColoring) + get_coloring(coloring.refColPack[], coloring.coloring) + coloring.coloring .+= Cint(1) + return coloring.coloring end diff --git a/src/colpack_partial_coloring.jl b/src/colpack_partial_coloring.jl new file mode 100644 index 0000000..9a3530a --- /dev/null +++ b/src/colpack_partial_coloring.jl @@ -0,0 +1,97 @@ +""" + ColPackPartialColoring + +Struct holding the parameters of a partial coloring as well as its results (which can be queried with [`get_colors`](@ref)). + +# Fields + +The fields of this struct are not part of the public API, they are only useful to interface with the C++ library [ColPack](https://github.com/CSCsw/ColPack). + +# Constructors + + ColPackPartialColoring( + filename::AbstractString, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false + ) + + ColPackPartialColoring( + M::SparseMatrixCSC, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false + ) + +Perform the partial coloring of a matrix that is either given directly or read from a file. + +The users needs to specify a partial coloring `method` and an `order` on the vertices. + +# See also + +- [`ColoringMethod`](@ref) +- [`ColoringOrder`](@ref) +""" +mutable struct ColPackPartialColoring + refColPack::Base.RefValue{Ptr{Cvoid}} + coloring::Vector{Cint} + method::ColoringMethod + order::ColoringOrder +end + +Base.unsafe_convert(::Type{Ptr{Cvoid}}, coloring::ColPackPartialColoring) = coloring.refColPack[] + +function ColPackPartialColoring( + filename::AbstractString, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false, +) + refColPack = Ref{Ptr{Cvoid}}(C_NULL) + reflen = Ref{Cint}(0) + ret = build_partial_coloring_from_file(refColPack, reflen, filename, method.method, order.order, verbose) + (ret == 0) && error("ColPack partial coloring failed.") + coloring = zeros(Cint, reflen[]) + g = ColPackPartialColoring(refColPack, coloring, method, order) + finalizer(free_partial_coloring, g) + return g +end + +function ColPackPartialColoring( + M::SparseMatrixCSC, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false, +) + reflen = Ref{Cint}(0) + refColPack = Ref{Ptr{Cvoid}}(C_NULL) + nrows, ncols = size(M) + + # The CSC format of M is the CSR format of Máµ€. + Mt_cols = Cint.(M.rowval) + Mt_rows = Cint.(M.colptr) + + # ColPack expects sparse CSR matrices with 0-based indexing. + Mt_cols .-= Cint(1) + Mt_rows .-= Cint(1) + + (method.method == "ROW_PARTIAL_DISTANCE_TWO") && (colpack_method = "COLUMN_PARTIAL_DISTANCE_TWO") + (method.method == "COLUMN_PARTIAL_DISTANCE_TWO") && (colpack_method = "ROW_PARTIAL_DISTANCE_TWO") + ret = build_partial_coloring_from_csr(refColPack, reflen, Mt_rows, Mt_cols, ncols, nrows, colpack_method, order.order, verbose) + (ret == 0) && error("ColPack partial coloring failed.") + coloring = zeros(Cint, reflen[]) + g = ColPackPartialColoring(refColPack, coloring, ColoringMethod(colpack_method), order) + finalizer(free_partial_coloring, g) + return g +end + +""" + get_colors(coloring::ColPackPartialColoring) + +Retrieve the colors from a [`ColPackPartialColoring`](@ref) as a vector of integers. +""" +function get_colors(coloring::ColPackPartialColoring) + get_partial_coloring(coloring.refColPack[], coloring.coloring) + coloring.coloring .+= Cint(1) + return coloring.coloring +end diff --git a/src/libcolpack.jl b/src/libcolpack.jl new file mode 100644 index 0000000..300f33a --- /dev/null +++ b/src/libcolpack.jl @@ -0,0 +1,58 @@ +function build_coloring_from_file(ref, len, _filename, _method, _order, verbose) + @ccall libcolpack.build_coloring_from_file(ref::Ptr{Ptr{Cvoid}}, len::Ptr{Cint}, _filename::Ptr{Cchar}, + _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_partial_coloring_from_file(ref, len, _filename, _method, _order, verbose) + @ccall libcolpack.build_partial_coloring_from_file(ref::Ptr{Ptr{Cvoid}}, len::Ptr{Cint}, _filename::Ptr{Cchar}, + _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_bicoloring_from_file(ref, len1, len2, _filename, _method, _order, verbose) + @ccall libcolpack.build_bicoloring_from_file(ref::Ptr{Ptr{Cvoid}}, len1::Ptr{Cint}, len2::Ptr{Cint}, _filename::Ptr{Cchar}, + _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_coloring_from_adolc(ref, len, adolc, rowcount, _method, _order, verbose) + @ccall libcolpack.build_coloring_from_adolc(ref::Ptr{Ptr{Cvoid}}, len::Ptr{Cint}, adolc::Ptr{Ptr{Cuint}}, + rowcount::Cint, _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_partial_coloring_from_adolc(ref, len, adolc, rowcount, colcount, _method, _order, verbose) + @ccall libcolpack.build_partial_coloring_from_adolc(ref::Ptr{Ptr{Cvoid}}, len::Ptr{Cint}, adolc::Ptr{Ptr{Cuint}}, + rowcount::Cint, colcount::Cint, _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_bicoloring_from_adolc(ref, len1, len2, adolc, rowcount, colcount, _method, _order, verbose) + @ccall libcolpack.build_bicoloring_from_adolc(ref::Ptr{Ptr{Cvoid}}, len1::Ptr{Cint}, len2::Ptr{Cint}, adolc::Ptr{Ptr{Cuint}}, + rowcount::Cint, colcount::Cint, _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function build_partial_coloring_from_csr(ref, len, rows, cols, rowcount, colcount, _method, _order, verbose) + @ccall libcolpack.build_partial_coloring_from_csr(ref::Ptr{Ptr{Cvoid}}, len::Ptr{Cint}, rows::Ptr{Cint}, cols::Ptr{Cint}, + rowcount::Cint, colcount::Cint, _method::Ptr{Cchar}, _order::Ptr{Cchar}, verbose::Cint)::Cint +end + +function get_coloring(ref, coloring) + @ccall libcolpack.get_coloring(ref::Ptr{Cvoid}, coloring::Ptr{Cint})::Cvoid +end + +function get_partial_coloring(ref, coloring) + @ccall libcolpack.get_partial_coloring(ref::Ptr{Cvoid}, coloring::Ptr{Cint})::Cvoid +end + +function get_bicoloring(ref, left_coloring, right_coloring) + @ccall libcolpack.get_bicoloring(ref::Ptr{Cvoid}, left_coloring::Ptr{Cint}, right_coloring::Ptr{Cint})::Cvoid +end + +function free_coloring(ref) + @ccall libcolpack.free_coloring(ref::Ptr{Cvoid})::Cvoid +end + +function free_partial_coloring(ref) + @ccall libcolpack.free_partial_coloring(ref::Ptr{Cvoid})::Cvoid +end + +function free_bicoloring(ref) + @ccall libcolpack.free_bicoloring(ref::Ptr{Cvoid})::Cvoid +end diff --git a/src/method.jl b/src/method.jl index 63f4a63..08115a6 100644 --- a/src/method.jl +++ b/src/method.jl @@ -13,6 +13,12 @@ Represent a [ColPack](https://github.com/CSCsw/ColPack)-compatible [coloring met - [`d2_coloring()`](@ref) - [`acyclic_coloring()`](@ref) - [`star_coloring()`](@ref) +- [`row_partial_d2_coloring()`](@ref) +- [`column_partial_d2_coloring()`](@ref) +- [`implicit_star_bicoloring()`](@ref) +- [`explicit_star_bicoloring()`](@ref) +- [`explicit_modified_star_bicoloring()`](@ref) +- [`implicit_greedy_star_bicoloring()`](@ref) """ struct ColoringMethod method::String @@ -45,3 +51,45 @@ acyclic_coloring() = ColoringMethod("ACYCLIC") Shortcut for `ColoringMethod("STAR")`. """ star_coloring() = ColoringMethod("STAR") + +""" + row_partial_d2_coloring() + +Shortcut for `ColoringMethod("ROW_PARTIAL_DISTANCE_TWO")`. +""" +row_partial_d2_coloring() = ColoringMethod("ROW_PARTIAL_DISTANCE_TWO") + +""" + column_partial_d2_coloring() + +Shortcut for `ColoringMethod("COLUMN_PARTIAL_DISTANCE_TWO")`. +""" +column_partial_d2_coloring() = ColoringMethod("COLUMN_PARTIAL_DISTANCE_TWO") + +""" + implicit_star_bicoloring() + +Shortcut for `ColoringMethod("IMPLICIT_COVERING__STAR_BICOLORING")`. +""" +implicit_star_bicoloring() = ColoringMethod("IMPLICIT_COVERING__STAR_BICOLORING") + +""" + explicit_star_bicoloring() + +Shortcut for `ColoringMethod("EXPLICIT_COVERING__STAR_BICOLORING")`. +""" +explicit_star_bicoloring() = ColoringMethod("EXPLICIT_COVERING__STAR_BICOLORING") + +""" + explicit_modified_star_bicoloring() + +Shortcut for `ColoringMethod("EXPLICIT_COVERING__MODIFIED_STAR_BICOLORING")`. +""" +explicit_modified_star_bicoloring() = ColoringMethod("EXPLICIT_COVERING__MODIFIED_STAR_BICOLORING") + +""" + implicit_greedy_star_bicoloring() + +Shortcut for `ColoringMethod(IMPLICIT_COVERING__GREEDY_STAR_BICOLORING")`. +""" +implicit_greedy_star_bicoloring() = ColoringMethod("IMPLICIT_COVERING__GREEDY_STAR_BICOLORING") diff --git a/test/functionality.jl b/test/functionality.jl index e0f70d8..696acf3 100644 --- a/test/functionality.jl +++ b/test/functionality.jl @@ -21,7 +21,7 @@ push!(orderings, incidence_degree_ordering()) push!(orderings, distance_two_incidence_degree_ordering()) # push!(orderings, random_ordering()) -ncolors = Vector{Int}() +ncolors = Vector{Cint}() push!(ncolors, 7) push!(ncolors, 7) @@ -43,16 +43,28 @@ MatrixMarket.mmwrite(filename, A) verbose = false @testset "MatrixMarket API" begin - @testset "$ordering" for (i, ordering) in enumerate(orderings) + @testset "Coloring -- $ordering" for (i, ordering) in enumerate(orderings) coloring = ColPackColoring(filename, d1_coloring(), ordering; verbose=verbose) - @test length(unique(get_colors(coloring))) == ncolors[i] + @test maximum(get_colors(coloring)) == ncolors[i] + end + + @testset "Partial coloring -- $ordering" for (i, ordering) in enumerate(orderings) + row_coloring = ColPackPartialColoring(filename, row_partial_d2_coloring(), ordering; verbose=verbose) + column_coloring = ColPackPartialColoring(filename, column_partial_d2_coloring(), ordering; verbose=verbose) end end @testset "ADOL-C Compressed Row Storage" begin - @testset "$ordering" for (i, ordering) in enumerate(orderings) + @testset "Coloring -- $ordering" for (i, ordering) in enumerate(orderings) coloring = ColPackColoring(A, d1_coloring(), ordering; verbose=verbose) - @test length(unique(get_colors(coloring))) == ncolors[i] + @test maximum(get_colors(coloring)) == ncolors[i] + end +end + +@testset "CSR Storage" begin + @testset "Partial coloring -- $ordering" for (i, ordering) in enumerate(orderings) + row_coloring = ColPackPartialColoring(A, row_partial_d2_coloring(), ordering; verbose=verbose) + column_coloring = ColPackPartialColoring(A, column_partial_d2_coloring(), ordering; verbose=verbose) end end