diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 0000000..c743950 --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1 @@ +style = "blue" \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..700707c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 0000000..440063a --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,29 @@ +name: Documentation + +on: + push: + branches: + - master # update to match your development branch (master, main, dev, trunk, ...) + tags: '*' + pull_request: + +jobs: + build: + permissions: + contents: write + pull-requests: read + statuses: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: '1' + - uses: julia-actions/cache@v2 + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key + run: julia --project=docs/ docs/make.jl diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 778c06f..4bad0ec 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,22 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: "3" +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' @@ -12,3 +28,6 @@ jobs: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + # Edit the following line to reflect the actual name of the GitHub Secret containing your private key + ssh: ${{ secrets.DOCUMENTER_KEY }} + # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml new file mode 100644 index 0000000..4f55f96 --- /dev/null +++ b/.github/workflows/Test.yml @@ -0,0 +1,35 @@ +name: Test + +on: + push: + branches: + - master + pull_request: + +# needed to allow julia-actions/cache to delete old caches that it has created +permissions: + actions: write + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + julia-version: ['1.6', '1'] + + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ matrix.julia-version }} + arch: x64 + - uses: julia-actions/cache@v2 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v4 + with: + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml deleted file mode 100644 index 02cf834..0000000 --- a/.github/workflows/action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Run tests - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened] - schedule: - - cron: '0 0 * * 0' - -jobs: - test-github-cpuonly: - env: - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-22.04] - julia-version: ['1.9'] - julia-arch: [x64] - - steps: - - uses: actions/checkout@v3 - - uses: julia-actions/setup-julia@latest - with: - version: ${{ matrix.julia-version }} - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest diff --git a/.gitignore b/.gitignore index 4fc6527..bd70be1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ Manifest.toml - +/docs/build/ +/docs/src/index.md +*.mtx \ No newline at end of file diff --git a/Project.toml b/Project.toml index 7664883..5bf5507 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ColPack" uuid = "ffa27691-3a59-46ab-a8d4-551f45b8d401" authors = ["Michel Schanen "] -version = "0.3.0" +version = "0.4.0" [deps] ColPack_jll = "f218ff0c-cb54-5151-80c4-c0f62c730ce6" @@ -11,4 +11,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] ColPack_jll = "0.3.0" +LinearAlgebra = "1" +Random = "1" +SparseArrays = "1" julia = "1.6" diff --git a/README.md b/README.md index da91ee3..cc16d8c 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,22 @@ # ColPack -[![][build-stable-img]][build-url] -This is the Julia interface to [ColPack](https://github.com/CSCsw/ColPack). +[![Build Status](https://github.com/michel2323/ColPack.jl/actions/workflows/Test.yml/badge.svg?branch=master)](https://github.com/michel2323/ColPack.jl/actions/workflows/Test.yml?query=branch%master) +[![Dev Documentation](https://img.shields.io/badge/docs-dev-blue.svg)](https://michel2323.github.io/ColPack.jl/dev/) -## Usage -### Jacobian coloring by columns -```julia -using ColPack -using SparseArrays - -# Example matrix/Jacobian -A = [ - [1.0 1.0 0.0 0.0 0.0]; - [0.0 0.0 1.0 0.0 0.0]; - [0.0 1.0 0.0 1.0 0.0]; - [0.0 0.0 0.0 1.0 1.0]; -] +This is the Julia interface to [ColPack](https://github.com/CSCsw/ColPack) for graph and matrix coloring. -A = sparse(A) +## Getting started -# Create adjacency matrix for column coloring -adjA = ColPack.matrix2adjmatrix(A; partition_by_rows=false) +You can install this package by running the following command in a Julia Pkg REPL (the necessary binaries will be downloaded automatically): -coloring = ColPackColoring(adjA, d1_coloring(), random_ordering()) -println("Number of colors: ", length(unique(get_colors(coloring)))) -println("Vector of vertex colors: ", get_colors(coloring)) +```julia +pkg> add ColPack ``` +Take a look at the tutorial in the documentation to get a feel for what you can do. + +## Mathematical background + +To understand the link between graph coloring and automatic differentiation, read the following survey: -[build-url]: https://github.com/michel2323/ColPack.jl/actions?query=workflow -[build-stable-img]: https://github.com/michel2323/ColPack.jl/workflows/Run%20tests/badge.svg?branch=master +> [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005) diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..8f22a0b --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +ColPack = "ffa27691-3a59-46ab-a8d4-551f45b8d401" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..b3f19eb --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,21 @@ +using Documenter +using ColPack + +cp(joinpath(@__DIR__, "..", "README.md"), joinpath(@__DIR__, "src", "index.md"); force=true) + +makedocs(; + modules=[ColPack], + authors="Michel Schanen", + sitename="ColPack.jl", + format=Documenter.HTML(), + pages=[ + "Home" => "index.md", + "Tutorial" => "tutorial.md", + "API reference" => "api.md", + ], +) + +deploydocs(; + repo="github.com/michel2323/ColPack.jl", + devbranch="master", +) diff --git a/docs/src/api.md b/docs/src/api.md new file mode 100644 index 0000000..19e72ec --- /dev/null +++ b/docs/src/api.md @@ -0,0 +1,36 @@ +```@meta +CollapsedDocStrings = true +``` + +# API reference + +## Entry points + +```@autodocs +Modules = [ColPack] +Pages = ["colpackcoloring.jl", "utils.jl"] +Private = false +``` + +## Coloring method + +```@autodocs +Modules = [ColPack] +Pages = ["method.jl"] +Private = false +``` + +## Coloring order + +```@autodocs +Modules = [ColPack] +Pages = ["order.jl"] +Private = false +``` + +## Internals + +```@autodocs +Modules = [ColPack] +Public = false +``` diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md new file mode 100644 index 0000000..947d69a --- /dev/null +++ b/docs/src/tutorial.md @@ -0,0 +1,44 @@ +# Tutorial + +## Jacobian coloring + +```jldoctest tuto +julia> using ColPack, SparseArrays +``` + +```jldoctest tuto +julia> J = sparse([ + 1 1 0 0 0 + 0 0 1 0 0 + 0 1 0 1 0 + 0 0 0 1 1 + ]); + +julia> adjJ = ColPack.matrix2adjmatrix(J; partition_by_rows=false) +5×5 SparseMatrixCSC{Float64, UInt32} with 6 stored entries: + ⋅ 1.0 ⋅ ⋅ ⋅ + 1.0 ⋅ ⋅ 1.0 ⋅ + ⋅ ⋅ ⋅ ⋅ ⋅ + ⋅ 1.0 ⋅ ⋅ 1.0 + ⋅ ⋅ ⋅ 1.0 ⋅ +``` + +```jldoctest tuto +julia> coloring = ColPackColoring(adjJ, d1_coloring(), natural_ordering()); + +julia> colors = get_colors(coloring) +5-element Vector{Int64}: + 1 + 2 + 1 + 1 + 2 + +julia> length(unique(colors)) == 2 +true +``` + +## Hessian coloring + +!!! warning + Work in progress \ No newline at end of file diff --git a/examples/coloring.jl b/examples/coloring.jl deleted file mode 100644 index 319112b..0000000 --- a/examples/coloring.jl +++ /dev/null @@ -1,32 +0,0 @@ -using ColPack -using MatrixMarket -using SparseArrays -using SparseDiffTools -using LinearAlgebra - -const filename = "/scratch/jac_case9241pegase_17036.mtx" - -orderings = Vector{String}() - -push!(orderings, "NATURAL") -push!(orderings, "LARGEST_FIRST") -push!(orderings, "DYNAMIC_LARGEST_FIRST") -push!(orderings, "DISTANCE_TWO_LARGEST_FIRST") -push!(orderings, "SMALLEST_LAST") -push!(orderings, "DISTANCE_TWO_SMALLEST_LAST") -push!(orderings, "INCIDENCE_DEGREE") -push!(orderings, "DISTANCE_TWO_INCIDENCE_DEGREE") -push!(orderings, "RANDOM") - -verbose = 1 -A = MatrixMarket.mmread(filename) - -@time sparsediff_coloring = SparseDiffTools.matrix_colors(A) -ncolor = size(unique(sparsediff_coloring),1) -@time colpack_coloring = ColPackColoring("/scratch/jac_case9241pegase_17036.mtx", ColPack.d1, "RANDOM", verbose) - -for ordering in orderings - colpack_coloring = ColPackColoring("/scratch/jac_case9241pegase_17036.mtx", ColPack.d1, ordering, verbose) -end -colors = get_colors(colpack_coloring) -@show length(unique(colors)) diff --git a/src/ColPack.jl b/src/ColPack.jl index c49b2b5..2916372 100644 --- a/src/ColPack.jl +++ b/src/ColPack.jl @@ -1,93 +1,30 @@ module ColPack +# Imports + using ColPack_jll using LinearAlgebra using SparseArrays -export ColPackColoring, get_colors, matrix2adjmatrix - -abstract type AbstractColoring end -include("colorings.jl") -export AbstractColoring - -abstract type AbstractOrdering end -include("orderings.jl") -export AbstractOrdering +# Definitions +include("method.jl") +include("order.jl") include("utils.jl") +include("colpackcoloring.jl") -mutable struct ColPackColoring - refColPack::Vector{Ptr{Cvoid}} - coloring::Vector{Cint} - method::AbstractColoring - order::AbstractOrdering - csr::Union{Vector{Ptr{Cuint}},Nothing} -end - -function free_coloring(g::ColPackColoring) - ccall( - (:free_coloring, libcolpack), - Cvoid, (Ptr{Cvoid},), - g.refColPack, - ) - return nothing -end +# Exports -function ColPackColoring(filename::AbstractString, method::AbstractColoring, order::AbstractOrdering; verbose::Bool=false) - reflen = Vector{Cint}([Cint(0)]) - refColPack = Vector{Ptr{Cvoid}}([C_NULL]) - ret = ccall( - (:build_coloring, libcolpack), - Cint, (Ptr{Cvoid}, Ptr{Cint}, Cstring, Cstring, Cstring, Cint), - refColPack, reflen, filename, method.colpack_coloring, order.colpack_ordering, Cint(verbose), - ) - if ret == 0 - error("ColPack coloring failed.") - end +export ColoringMethod +export d1_coloring, d2_coloring, acyclic_coloring, star_coloring - g = ColPackColoring(refColPack, zeros(Int, reflen[1]), method, order, nothing) - finalizer(free_coloring, g) - return g -end +export ColoringOrder +export natural_ordering, largest_first_ordering, dynamic_largest_first_ordering, distance_two_largest_first_ordering +export smallest_last_ordering, distance_two_smallest_last_ordering, incidence_degree_ordering, distance_two_incidence_degree_ordering +export random_ordering -function ColPackColoring(M::SparseMatrixCSC{VT,IT}, method::AbstractColoring, order::AbstractOrdering; verbose::Bool=false) where {VT,IT} - @assert issymmetric(M) - csr = Vector{Ref{Cuint}}() - csr_mem = Vector{Vector{Cuint}}() - for i in 1:(length(M.colptr) -1) - vec = Vector{Cuint}() - # Number of column elements - push!(vec, Cuint(M.colptr[i+1] - M.colptr[i])) - for j in M.colptr[i]:(M.colptr[i+1]-1) - push!(vec, Cuint(M.rowval[j]-1)) - end - push!(csr, Base.unsafe_convert(Ptr{Cuint}, vec)) - push!(csr_mem, vec) - end - nrows = size(M,2) - reflen = Vector{Cint}([Cint(0)]) - refColPack = Vector{Ptr{Cvoid}}([C_NULL]) - ret = ccall( - (:build_coloring_from_csr, libcolpack), - Cint, (Ptr{Cvoid}, Ptr{Cint}, Ref{Ptr{Cuint}}, Cint, Cstring, Cstring, Cint), - refColPack, reflen, csr, nrows, method.colpack_coloring, order.colpack_ordering, Cint(verbose), - ) - if ret == 0 - error("ColPack coloring failed.") - end +export matrix2adjmatrix - g = ColPackColoring(refColPack, zeros(Int, reflen[1]), method, order, csr) - finalizer(free_coloring, g) - return g -end +export ColPackColoring, get_colors -function get_colors(coloring::ColPackColoring; verbose=false) - ccall( - (:get_colors, libcolpack), - Cvoid, (Ptr{Cvoid}, Ptr{Cdouble}, Cstring, Cint), - coloring.refColPack[1], coloring.coloring, coloring.method.colpack_coloring, Cint(verbose) - ) - # Julia colorings should be base 1 - return coloring.coloring .+ 1 -end end #module diff --git a/src/colorings.jl b/src/colorings.jl deleted file mode 100644 index 1ced3ac..0000000 --- a/src/colorings.jl +++ /dev/null @@ -1,30 +0,0 @@ -struct d1_coloring <: AbstractColoring - colpack_coloring::String -end - -struct d2_coloring <: AbstractColoring - colpack_coloring::String -end - -struct acyclic_coloring <: AbstractColoring - colpack_coloring::String -end - -struct star_coloring <: AbstractColoring - colpack_coloring::String -end - -d1_coloring() = d1_coloring("DISTANCE_ONE") -d2_coloring()= d2_coloring("DISTANCE_TWO") -acyclic_coloring() = acyclic_coloring("ACYCLIC") -star_coloring() = star_coloring("STAR") - -COLORINGS = [ - d1_coloring(), - d2_coloring(), - acyclic_coloring(), - star_coloring() -] - -export COLORINGS -export d1_coloring, d2_coloring, acyclic_coloring, star_coloring diff --git a/src/colpackcoloring.jl b/src/colpackcoloring.jl new file mode 100644 index 0000000..068eca6 --- /dev/null +++ b/src/colpackcoloring.jl @@ -0,0 +1,127 @@ +""" + ColPackColoring + +Struct holding the parameters of a 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 + + ColPackColoring( + filename::AbstractString, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false + ) + + ColPackColoring( + 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 coloring `method` and an `order` on the vertices. + +# See also + +- [`ColoringMethod`](@ref) +- [`ColoringOrder`](@ref) +""" +mutable struct ColPackColoring + refColPack::Base.RefValue{Ptr{Cvoid}} + 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 + +function ColPackColoring( + filename::AbstractString, + method::ColoringMethod, + 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) + finalizer(free_coloring, g) + return g +end + +function ColPackColoring( + M::SparseMatrixCSC{VT,IT}, + method::ColoringMethod, + order::ColoringOrder; + verbose::Bool=false, +) where {VT,IT} + @assert issymmetric(M) + csr = Vector{Ref{Cuint}}() + csr_mem = Vector{Vector{Cuint}}() + for i in 1:(length(M.colptr) - 1) + vec = Vector{Cuint}() + # Number of column elements + push!(vec, Cuint(M.colptr[i + 1] - M.colptr[i])) + for j in M.colptr[i]:(M.colptr[i + 1] - 1) + push!(vec, Cuint(M.rowval[j] - 1)) + end + push!(csr, Base.unsafe_convert(Ptr{Cuint}, vec)) + push!(csr_mem, vec) + end + 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 + + g = ColPackColoring(refColPack, zeros(Int, reflen[]), method, order, csr) + finalizer(free_coloring, g) + return g +end + +""" + get_colors(coloring::ColPackColoring; verbose=false) + +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 +end diff --git a/src/method.jl b/src/method.jl new file mode 100644 index 0000000..63f4a63 --- /dev/null +++ b/src/method.jl @@ -0,0 +1,47 @@ +""" + ColoringMethod + +Represent a [ColPack](https://github.com/CSCsw/ColPack)-compatible [coloring method](https://cscapes.cs.purdue.edu/coloringpage/software.htm#capabilities). + +# Fields + +- `method::String` + +# Constructors + +- [`d1_coloring()`](@ref) +- [`d2_coloring()`](@ref) +- [`acyclic_coloring()`](@ref) +- [`star_coloring()`](@ref) +""" +struct ColoringMethod + method::String +end + +""" + d1_coloring() + +Shortcut for `ColoringMethod("DISTANCE_ONE")`. +""" +d1_coloring() = ColoringMethod("DISTANCE_ONE") + +""" + d2_coloring() + +Shortcut for `ColoringMethod("DISTANCE_TWO")`. +""" +d2_coloring() = ColoringMethod("DISTANCE_TWO") + +""" + acyclic_coloring() + +Shortcut for `ColoringMethod("ACYCLIC")`. +""" +acyclic_coloring() = ColoringMethod("ACYCLIC") + +""" + star_coloring() + +Shortcut for `ColoringMethod("STAR")`. +""" +star_coloring() = ColoringMethod("STAR") diff --git a/src/order.jl b/src/order.jl new file mode 100644 index 0000000..b59bdbe --- /dev/null +++ b/src/order.jl @@ -0,0 +1,87 @@ +""" + ColoringOrder + +Represent a [ColPack](https://github.com/CSCsw/ColPack)-compatible [coloring order](https://cscapes.cs.purdue.edu/coloringpage/software.htm#Ordering). + +# Fields + +- `order::String` + +# Constructors + +- [`natural_ordering()`](@ref) +- [`largest_first_ordering()`](@ref) +- [`dynamic_largest_first_ordering()`](@ref) +- [`distance_two_largest_first_ordering()`](@ref) +- [`smallest_last_ordering()`](@ref) +- [`distance_two_smallest_last_ordering()`](@ref) +- [`incidence_degree_ordering()`](@ref) +- [`distance_two_incidence_degree_ordering()`](@ref) +- [`random_ordering()`](@ref) +""" +struct ColoringOrder + order::String +end + +""" + natural_ordering() + +Shortcut for `ColoringOrder("NATURAL")`. +""" +natural_ordering() = ColoringOrder("NATURAL") + +""" + largest_first_ordering() + +Shortcut for `ColoringOrder("LARGEST_FIRST")`. +""" +largest_first_ordering() = ColoringOrder("LARGEST_FIRST") + +""" + dynamic_largest_first_ordering() + +Shortcut for `ColoringOrder("DYNAMIC_LARGEST_FIRST")`. +""" +dynamic_largest_first_ordering() = ColoringOrder("DYNAMIC_LARGEST_FIRST") + +""" + distance_two_largest_first_ordering() + +Shortcut for `ColoringOrder("DISTANCE_TWO_LARGEST_FIRST")`. +""" +distance_two_largest_first_ordering() = ColoringOrder("DISTANCE_TWO_LARGEST_FIRST") + +""" + smallest_last_ordering() + +Shortcut for `ColoringOrder("SMALLEST_LAST")`. +""" +smallest_last_ordering() = ColoringOrder("SMALLEST_LAST") + +""" + distance_two_smallest_last_ordering() + +Shortcut for `ColoringOrder("DISTANCE_TWO_SMALLEST_LAST")`. +""" +distance_two_smallest_last_ordering() = ColoringOrder("DISTANCE_TWO_SMALLEST_LAST") + +""" + incidence_degree_ordering() + +Shortcut for `ColoringOrder("INCIDENCE_DEGREE")`. +""" +incidence_degree_ordering() = ColoringOrder("INCIDENCE_DEGREE") + +""" + distance_two_incidence_degree_ordering() + +Shortcut for `ColoringOrder("DISTANCE_TWO_INCIDENCE_DEGREE")`. +""" +distance_two_incidence_degree_ordering() = ColoringOrder("DISTANCE_TWO_INCIDENCE_DEGREE") + +""" + random_ordering() + +Shortcut for `ColoringOrder("RANDOM")`. +""" +random_ordering() = ColoringOrder("RANDOM") diff --git a/src/orderings.jl b/src/orderings.jl deleted file mode 100644 index 0b932f4..0000000 --- a/src/orderings.jl +++ /dev/null @@ -1,61 +0,0 @@ -struct natural_ordering <: AbstractOrdering - colpack_ordering::String -end -natural_ordering() = natural_ordering("NATURAL") - -struct largest_first_ordering <: AbstractOrdering - colpack_ordering::String -end -largest_first_ordering() = largest_first_ordering("LARGEST_FIRST") - -struct dynamic_largest_first_ordering <: AbstractOrdering - colpack_ordering::String -end -dynamic_largest_first_ordering() = dynamic_largest_first_ordering("DYNAMIC_LARGEST_FIRST") - -struct distance_two_largest_first_ordering <: AbstractOrdering - colpack_ordering::String -end -distance_two_largest_first_ordering() = distance_two_largest_first_ordering("DISTANCE_TWO_LARGEST_FIRST") - -struct smallest_last_ordering <: AbstractOrdering - colpack_ordering::String -end -smallest_last_ordering() = smallest_last_ordering("SMALLEST_LAST") - -struct distance_two_smallest_last_ordering <: AbstractOrdering - colpack_ordering::String -end -distance_two_smallest_last_ordering() = distance_two_smallest_last_ordering("DISTANCE_TWO_SMALLEST_LAST") - -struct incidence_degree_ordering <: AbstractOrdering - colpack_ordering::String -end -incidence_degree_ordering() = incidence_degree_ordering("INCIDENCE_DEGREE") - -struct distance_two_incidence_degree_ordering <: AbstractOrdering - colpack_ordering::String -end -distance_two_incidence_degree_ordering() = distance_two_incidence_degree_ordering("DISTANCE_TWO_INCIDENCE_DEGREE") - -struct random_ordering <: AbstractOrdering - colpack_ordering::String -end -random_ordering() = random_ordering("RANDOM") - -const ORDERINGS = [ - distance_two_incidence_degree_ordering(), - distance_two_largest_first_ordering(), - distance_two_smallest_last_ordering(), - dynamic_largest_first_ordering(), - incidence_degree_ordering(), - largest_first_ordering(), - natural_ordering(), - smallest_last_ordering(), - random_ordering(), -] - -export natural_ordering, largest_first_ordering, dynamic_largest_first_ordering, distance_two_largest_first_ordering -export smallest_last_ordering, distance_two_smallest_last_ordering, incidence_degree_ordering, distance_two_incidence_degree_ordering -export random_ordering -export ORDERINGS diff --git a/src/utils.jl b/src/utils.jl index d9e841d..b933cee 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,5 +1,6 @@ """ - _cols_by_rows(rows_index,cols_index) + _cols_by_rows(rows_index, cols_index) + Returns a vector of rows where each row contains a vector of its column indices. """ @@ -12,9 +13,9 @@ function _cols_by_rows(rows_index,cols_index) return cols_by_rows end - """ - _rows_by_cols(rows_index,cols_index) + _rows_by_cols(rows_index, cols_index) + Returns a vector of columns where each column contains a vector of its row indices. """ @@ -22,7 +23,12 @@ function _rows_by_cols(rows_index,cols_index) return _cols_by_rows(cols_index,rows_index) end -function matrix2adjmatrix(M; partition_by_rows = true) +""" + matrix2adjmatrix(M::AbstractMatrix; partition_by_rows=true) + +Create an adjacency matrix between the rows or between the columns of `M`, depending on whether `partition_by_rows` is `true` or `false`. +""" +function matrix2adjmatrix(M::AbstractMatrix; partition_by_rows = true) (rows_index, cols_index, _) = findnz(M) if partition_by_rows A = SparseMatrixCSC{Float64, Cuint}(size(M,1), size(M,1), ones(Cuint, size(M,1)+1), Vector{Cuint}(), Vector{Float64}()) diff --git a/test/Project.toml b/test/Project.toml index 037f8ee..02cb6af 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,7 +1,11 @@ [deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MatrixMarket = "4d4711f2-db25-561a-b6b3-d35e7d4047d3" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/functionality.jl b/test/functionality.jl new file mode 100644 index 0000000..e0f70d8 --- /dev/null +++ b/test/functionality.jl @@ -0,0 +1,72 @@ +using ColPack +using LinearAlgebra +using MatrixMarket +using Random +using SparseArrays +using SparseDiffTools +using StableRNGs +using Test + +rng = StableRNG(63) + +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()) + +ncolors = Vector{Int}() + +push!(ncolors, 7) +push!(ncolors, 7) +push!(ncolors, 6) +push!(ncolors, 7) # changed +push!(ncolors, 6) +push!(ncolors, 6) +push!(ncolors, 6) # changed +push!(ncolors, 6) +# push!(ncolors, 10) + +Random.seed!(2713) +# Create adjacency graph +# TODO: random is a bad idea, nb of colors may change (temporarily fixed with StableRNGs) +A = convert(SparseMatrixCSC, Symmetric(sprand(rng, 100, 100, 0.1))) + +const filename = joinpath(@__DIR__, "A.mtx") +MatrixMarket.mmwrite(filename, A) +verbose = false + +@testset "MatrixMarket API" begin + @testset "$ordering" for (i, ordering) in enumerate(orderings) + coloring = ColPackColoring(filename, d1_coloring(), ordering; verbose=verbose) + @test length(unique(get_colors(coloring))) == ncolors[i] + end +end + +@testset "ADOL-C Compressed Row Storage" begin + @testset "$ordering" for (i, ordering) in enumerate(orderings) + coloring = ColPackColoring(A, d1_coloring(), ordering; verbose=verbose) + @test length(unique(get_colors(coloring))) == ncolors[i] + end +end + +@testset "ColPack Columns Coloring" begin + A = [ + [1.0 1.0 0.0 0.0 0.0] + [0.0 0.0 1.0 0.0 0.0] + [0.0 1.0 0.0 1.0 0.0] + [0.0 0.0 0.0 1.0 1.0] + ] + + A = sparse(A) + adjA = ColPack.matrix2adjmatrix(A; partition_by_rows=false) + @test issymmetric(adjA) + coloring = ColPackColoring(adjA, d1_coloring(), natural_ordering(); verbose=true) + @test get_colors(coloring) == SparseDiffTools.matrix_colors(A) +end diff --git a/test/runtests.jl b/test/runtests.jl index 45947cc..9298925 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,68 +1,22 @@ +using Aqua using ColPack -using LinearAlgebra -using MatrixMarket -using Random -using SparseArrays -using SparseDiffTools +using Documenter +using JET using Test -orderings = Vector{AbstractOrdering}() - -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") - -ncolors = Vector{Int}() - -push!(ncolors, 7) -push!(ncolors, 7) -push!(ncolors, 6) -push!(ncolors, 6) -push!(ncolors, 6) -push!(ncolors, 6) -push!(ncolors, 7) -push!(ncolors, 6) -# push!(ncolors, 10) - -Random.seed!(2713) -# Create adjacency graph -A = convert(SparseMatrixCSC, Symmetric(sprand(100,100,0.1))) - -const filename = joinpath(@__DIR__, "A.mtx") -MatrixMarket.mmwrite(filename, A) -verbose = false - -@testset "Test ColPack" begin - @testset "Test Matrix Market API" begin - for (i,ordering) in enumerate(orderings) - coloring = ColPackColoring(filename, d1_coloring(), ordering; verbose=verbose) - @test length(unique(get_colors(coloring))) == ncolors[i] +@testset verbose = true "ColPack" begin + if VERSION >= v"1.10" + @testset verbose = false "Code quality" begin + Aqua.test_all(ColPack) + @testset "JET" begin + JET.test_package(ColPack; target_defined_modules=true) + end end end - @testset "Test ADOL-C Compressed Row Storage" begin - for (i,ordering) in enumerate(orderings) - coloring = ColPackColoring(A, d1_coloring(), ordering; verbose=verbose) - @test length(unique(get_colors(coloring))) == ncolors[i] - end + @testset "Doctests" begin + Documenter.doctest(ColPack) end - @testset "Test ColPack Columns Coloring" begin - A = [ - [1.0 1.0 0.0 0.0 0.0]; - [0.0 0.0 1.0 0.0 0.0]; - [0.0 1.0 0.0 1.0 0.0]; - [0.0 0.0 0.0 1.0 1.0]; - ] - - A = sparse(A) - adjA = ColPack.matrix2adjmatrix(A; partition_by_rows=false) - @test issymmetric(adjA) - coloring = ColPackColoring(adjA, d1_coloring(), natural_ordering(); verbose=true) - @test get_colors(coloring) == SparseDiffTools.matrix_colors(A) + @testset verbose = true "Functionality" begin + include("functionality.jl") end end