Table logging in Julia

This is kinda part of the constraint solver series => Building a constraint solver in Julia but is useful for more than this and might be its own package (yeah I say that often :D)

There were some changes to the constraint solver which still need a post and I’m not satisfied yet to write a post about it but I should…

What is this topic about?

When I started with Julia and Juniper.jl I needed to write a table logger to display the current status of the solver.
By status I mean information like best solution found so far (incumbent) and best bound as well as how long did it run so far.

Basically the table from this:

Logging of Juniper.jl

Now I of course also need this for the constraint solver project but I didn’t like the code that much and spoiler alert: I’m not completely satisfied with the current implementation now but I think I should put it out here and maybe some others have ideas on how to improve it.

What I want

I want to easily define the table by giving the header information which includes the type, the width and some other information.

In the solver I want to push something to the table and given some criteria the table should decide whether this is a new row or whether there it’s not worth it i.e in the constraint solver there can be a lot of backtracking steps but they can be quite fast for example in the case of solving sudokus. I don’t want a line for each step in the backtrack! function. Normally the user wants to get updates to see that it’s running or if a new solution was found. Therefore I use the rule that a new line should be added every five seconds or if there is a new solution.

Additionally the type of the column should define some formatting rules which the developer should be able to change.

Some problems

Not too sure how to make it as extensible as possible so this is my try of doing it and I welcome your comments/issues/PRs.

Current implementation

TableLogger.jl

mutable struct TableCol
    id                  :: Symbol
    name                :: String
    type                :: DataType
    width               :: Int
    alignment           :: Symbol # :left, :center, :right
    b_format            :: Bool
end

mutable struct TableEntry{T}
    col_id              :: Symbol
    value               :: T
end

mutable struct TableSetup
    cols                :: Vector{TableCol}
    col_idx             :: Dict{Symbol,Int}
    new_row_criteria    :: Bool
    diff_criteria       :: Dict{Symbol, Any}
    last_row            :: Vector{TableEntry}
end

The table is defined by the columns cols might have some criteria to check if a new row should be added or now which depends on last_row and diff_criteria will be passed to the function of deciding whether a new row will be added.
I need the col_idx to know which column is at which position. Maybe that can be done in a different fashion.

TableCol has a type which defines whether some formatting should be used.
Each row is a vector of TableEntry which defines the column…