Visualization

SparseMatrixColorings provides some internal utilities for visualization of matrix colorings via the un-exported function SparseMatrixColorings.show_colors.

Warning

This function makes use of the JuliaImages ecosystem. Using it requires loading at least Colors.jl. We recommend loading the full Images.jl package for convenience, which includes Colors.jl.

using ColorSchemes
using Images
using SparseArrays
using SparseMatrixColorings
using SparseMatrixColorings: show_colors
using StableRNGs

Basic usage

To obtain a visualization, simply call show_colors on a coloring result. It returns a tuple of outputs, corresponding to the matrix and its compression(s):

S = sparse([
    0 0 1 1 0 1
    1 0 0 0 1 0
    0 1 0 0 1 0
    0 1 1 0 0 0
]);

problem = ColoringProblem(; structure=:nonsymmetric, partition=:column)
algo = GreedyColoringAlgorithm(; decompression=:direct)
result = coloring(S, problem, algo)

A_img, B_img = show_colors(result; scale=3)
(ColorTypes.RGBA{FixedPointNumbers.N0f8}[RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0) … RGBA(0.0, 0.827, 1.0, 1.0) RGBA(0.0, 0.827, 1.0, 1.0); RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0) … RGBA(0.0, 0.827, 1.0, 1.0) RGBA(0.0, 0.827, 1.0, 1.0); … ; RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0) … RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0); RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0) … RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0)], ColorTypes.RGBA{FixedPointNumbers.N0f8}[RGBA(1.0, 1.0, 0.455, 1.0) RGBA(1.0, 1.0, 0.455, 1.0) … RGBA(0.0, 0.827, 1.0, 1.0) RGBA(0.0, 0.827, 1.0, 1.0); RGBA(1.0, 1.0, 0.455, 1.0) RGBA(1.0, 1.0, 0.455, 1.0) … RGBA(0.0, 0.827, 1.0, 1.0) RGBA(0.0, 0.827, 1.0, 1.0); … ; RGBA(1.0, 1.0, 0.455, 1.0) RGBA(1.0, 1.0, 0.455, 1.0) … RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0); RGBA(1.0, 1.0, 0.455, 1.0) RGBA(1.0, 1.0, 0.455, 1.0) … RGBA(0.0, 0.0, 0.0, 0.0) RGBA(0.0, 0.0, 0.0, 0.0)])

The colors on the original matrix look like this:

A_img

And its column compression looks like that:

B_img
Terminal support

Loading ImageInTerminal.jl will allow you to show the output of show_colors within your terminal. If you use VSCode's Julia REPL, the matrix will be displayed in the plot tab.

Customization

The visualization can be customized via keyword arguments. The size of the matrix entries is defined by scale, while gaps between them are dictated by pad. We recommend using the ColorSchemes.jl catalogue to customize the colorscheme. Finally, a background color can be passed via the background keyword argument. To obtain transparent backgrounds, use the RGBA type.

We demonstrate this on a bidirectional coloring.

S = sparse([
    1 1 1 1 1 1 1
    1 0 0 0 0 0 1
    1 0 0 0 0 0 1
    1 0 0 0 0 0 1
    1 1 1 1 1 1 1
])

problem_bi = ColoringProblem(; structure=:nonsymmetric, partition=:bidirectional)
algo_bi = GreedyColoringAlgorithm(RandomOrder(StableRNG(0)); decompression=:direct)
result_bi = coloring(S, problem_bi, algo_bi)

A_img, Br_img, Bc_img = show_colors(
    result_bi;
    colorscheme=ColorSchemes.progress,
    background=RGB(1, 1, 1),  # white
    scale=10,
    pad=2
)
(ColorTypes.RGB{FixedPointNumbers.N0f8}[RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); … ; RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0)], ColorTypes.RGB{FixedPointNumbers.N0f8}[RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); … ; RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0)], ColorTypes.RGB{FixedPointNumbers.N0f8}[RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); … ; RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0); RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0) … RGB(1.0, 1.0, 1.0) RGB(1.0, 1.0, 1.0)])

In the bidirectional case, columns and rows can both get colors:

A_img

And there are two compression results, one by row and one by column:

Br_img
Bc_img

Working with large matrices

Let's demonstrate visualization of a larger random matrix:

S = sprand(50, 50, 0.1) # sample sparse matrix

problem = ColoringProblem(; structure=:nonsymmetric, partition=:column)
algo = GreedyColoringAlgorithm(; decompression=:direct)
result = coloring(S, problem, algo)
show_colors(result; scale=5, pad=1)[1]

Instead of the default distinguishable_colors from Colors.jl, one can subsample a continuous colorscheme from ColorSchemes.jl:

ncolors = maximum(column_colors(result)) # for partition=:column
colorscheme = get(ColorSchemes.rainbow, range(0.0, 1.0, length=ncolors))
show_colors(result; colorscheme=colorscheme, scale=5, pad=1)[1]

Saving images

The resulting image can be saved to a variety of formats, like PNG. The scale and pad parameters determine the number of pixels, and thus the size of the file.

A_img, _ = show_colors(result, scale=5)
save("coloring.png", A_img)

Refer to the JuliaImages documentation on saving for more information.