Utility package for efficient tracking of optimization histories, training curves or other information of arbitrary types and at arbitrarily spaced sampling times
Package License | PkgEval (Nanosoldier) | Build Status |
---|---|---|
This package is registered in METADATA.jl
and can be installed as usual
pkg> add ValueHistories
We provide two basic approaches for logging information over time or iterations. The sample points do not have to be equally spaced as long as time/iteration is strictly increasing.
- Univalue histories: Intended for tracking the evolution of a single value over time.
- Multivalue histories: Track an arbitrary amount of values over time, each of which can be of a different type and associated with a label
Note that both approaches are typestable.
This package provide two different concrete implementations
QHistory
: Logs the values using aDequeue
History
: Logs the values using aVector
Supported operations for univalue histories:
push!(history, iteration, value)
: Appends a value to the historyget(history)
: Returns all available observations as two vectors. The first vector contains the iterations and the second vector contains the values.enumerate(history)
Returns an enumerator over the observations (as tuples)first(history)
: First stored observation (as tuple)last(history)
: Last stored observation (as tuple)length(history)
: Number of stored observationsincrement!(history, iteration, value)
: Similar topush!
but increments thevalue
if theiteration
already exists. Only supported byHistory
.
Here is a little example code showing the basic usage:
using Primes
# Specify the type of value you wish to track
history = QHistory(Float64)
for i = 1:100
# Store some value of the specified type
# Note how the sampling times are not equally spaced
isprime(i) && push!(history, i, sin(.1*i))
end
# Access stored values as arrays
x, y = get(history)
@assert typeof(x) <: Vector{Int}
@assert typeof(y) <: Vector{Float64}
# You can also enumerate over the observations
for (x, y) in enumerate(history)
@assert typeof(x) <: Int
@assert typeof(y) <: Float64
end
# Let's see how this prints to the REPL
history
QHistory
types: Int64, Float64
length: 25
For easy visualisation we also provide recipes for Plots.jl
.
Note that this is only supported for Real
types.
using Plots
plot(history, legend=false)
Multivalue histories are more or less a dynamic collection of a number
of univalue histories. Each individual univalue history is associated
with a symbol key
. If the user stores a value under a key
that
has no univalue history associated with it, then a new one is allocated
and specialized for the given type.
Supported operations for multivalue histories:
push!(history, key, iteration, value)
: Appends a value to the multivalue historyget(history, key)
: Returns all available observations as two vectors. The first vector contains the iterations and the second vector contains the values.enumerate(history, key)
Returns an enumerator over the observations (as tuples)first(history, key)
: First stored observation (as tuple)last(history, key)
: Last stored observation (as tuple)length(history, key)
: Number of stored observationsincrement!(history, key, iteration, value)
: Similar topush!
but increments thevalue
if thekey
anditeration
combination already exists.
Here is a little example code showing the basic usage:
using ValueHistories, Primes
history = MVHistory()
for i=1:100
x = 0.1i
# Store any kind of value without losing type stability
# The first push! to a key defines the tracked type
# push!(history, key, iter, value)
push!(history, :mysin, x, sin(x))
push!(history, :mystring, i, "i=$i")
# Sampling times can be arbitrarily spaced
# Note how we store the sampling time as a Float32 this time
isprime(i) && push!(history, :mycos, Float32(x), cos(x))
end
# Access stored values as arrays
x, y = get(history, :mysin)
@assert length(x) == length(y) == 100
@assert typeof(x) <: Vector{Float64}
@assert typeof(y) <: Vector{Float64}
# Each key can be queried individually
x, y = get(history, :mystring)
@assert length(x) == length(y) == 100
@assert typeof(x) <: Vector{Int64}
@assert typeof(y) <: Vector{String}
@assert y[1] == "i=1"
# You can also enumerate over the observations
for (x, y) in enumerate(history, :mycos)
@assert typeof(x) <: Float32
@assert typeof(y) <: Float64
end
# Let's see how this prints to the REPL
history
MVHistory{ValueHistories.History{I,V}}
:mysin => 100 elements {Float64,Float64}
:mystring => 100 elements {Int64,String}
:mycos => 25 elements {Float32,Float64}
For easy visualisation we also provide recipes for Plots.jl
.
Note that this is only supported for Real
types.
using Plots
plot(history)
This code is free to use under the terms of the MIT license.