Record values

To record values during the iterations of a solver run, there are in general two possibilities. On the one hand, the high-level interfaces provide a record= keyword, that accepts several different inputs. For more details see How to record.

Record Actions & the solver state decorator


A RecordAction is a small functor to record values. The usual call is given by

(amp::AbstractManoptProblem, ams::AbstractManoptSolverState, i) -> s

that performs the record for the current problem and solver cmbination, and where i is the current iteration.

By convention i=0 is interpreted as "For Initialization only," so only initialize internal values, but not trigger any record, that the record is called from within stop_solver! which returns true afterwards.

Any negative value is interpreted as a “reset”, and should hence delete all stored recordings, for example when reusing a RecordAction. The start of a solver calls the :Iteration and :Stop dictionary entries with -1, to reset those recordings.

By default any RecordAction is assumed to record its values in a field recorded_values, an Vector of recorded values. See get_record(ra).

RecordChange <: RecordAction

debug for the amount of change of the iterate (see get_iterate(s) of the AbstractManoptSolverState) during the last iteration.


  • storage : a StoreStateAction to store (at least) the last iterate to use this as the last value (to compute the change) serving as a potential cache shared with other components of the solver.
  • inverse_retraction_method : the inverse retraction to be used for approximating distance.
  • recorded_values : to store the recorded values


    inverse_retraction_method = default_inverse_retraction_method(M),
    storage                   = StoreStateAction(M; store_points=Tuple{:Iterate})

with the preceding fields as keywords. For the DefaultManifold only the field storage is used. Providing the actual manifold moves the default storage to the efficient point storage.

RecordCost <: RecordAction

Record the current cost function value, see get_cost.


  • recorded_values : to store the recorded values


RecordEntry{T} <: RecordAction

record a certain fields entry of type {T} during the iterates



RecordEntry(::T, f::Symbol)
RecordEntry(T::DataType, f::Symbol)

Initialize the record action to record the state field f, and initialize the recorded_values to be a vector of element type T.


  • RecordEntry(rand(M), :q) to record the points from M stored in some states s.q
  • RecordEntry(SVDMPoint, :p) to record the field s.p which takes values of type SVDMPoint.
RecordEntryChange{T} <: RecordAction

record a certain entries change during iterates

Additional fields

  • recorded_values : the recorded Iterates
  • field : Symbol the field can be accessed with within AbstractManoptSolverState
  • distance : function (p,o,x1,x2) to compute the change/distance between two values of the entry
  • storage : a StoreStateAction to store (at least) getproperty(o, d.field)


RecordEntryChange(f::Symbol, d, a::StoreStateAction=StoreStateAction([f]))
RecordEvery <: RecordAction

record only every $i$th iteration. Otherwise (optionally, but activated by default) just update internal tracking values.

This method does not perform any record itself but relies on it's children's methods

RecordGroup <: RecordAction

group a set of RecordActions into one action, where the internal RecordActions act independently, but the results can be collected in a grouped fashion, a tuple per calls of this group. The entries can be later addressed either by index or semantic Symbols


RecordGroup(g::Array{<:RecordAction, 1})

construct a group consisting of an Array of RecordActions g,

RecordGroup(g, symbols)


g1 = RecordGroup([RecordIteration(), RecordCost()])

A RecordGroup to record the current iteration and the cost. The cost can then be accessed using get_record(r,2) or r[2].

g2 = RecordGroup([RecordIteration(), RecordCost()], Dict(:Cost => 2))

A RecordGroup to record the current iteration and the cost, which can then be accessed using get_record(:Cost) or r[:Cost].

g3 = RecordGroup([RecordIteration(), RecordCost() => :Cost])

A RecordGroup identical to the previous constructor, just a little easier to use. To access all recordings of the second entry of this last g3 you can do either g4[2] or g[:Cost], the first one can only be accessed by g4[1], since no symbol was given here.

RecordIterate <: RecordAction

record the iterate



initialize the iterate record array to the type of x0, which indicates the kind of iterate


initialize the iterate record array to the data type T.

RecordSolverState <: AbstractManoptSolverState

append to any AbstractManoptSolverState the decorator with record capability, Internally a dictionary is kept that stores a RecordAction for several concurrent modes using a Symbol as reference. The default mode is :Iteration, which is used to store information that is recorded during the iterations. RecordActions might be added to :Start or :Stop to record values at the beginning or for the stopping time point, respectively

The original options can still be accessed using the get_state function.


  • options the options that are extended by debug information
  • recordDictionary a Dict{Symbol,RecordAction} to keep track of all different recorded values



construct record decorated AbstractManoptSolverState, where dR can be

  • a RecordAction, then it is stored within the dictionary at :Iteration
  • an Array of RecordActions, then it is stored as a recordDictionary(@ref).
  • a Dict{Symbol,RecordAction}.
RecordSubsolver <: RecordAction

Record the current subsolvers recording, by calling get_record on the substate with


  • records: an array to store the recorded values
  • symbols: arguments for get_record. Defaults to just one symbol :Iteration, but could be set to also record the :Stop action.


RecordSubsolver(; record=[:Iteration,], record_type=eltype([]))
RecordTime <: RecordAction

record the time elapsed during the current iteration.

The three possible modes are

  • :cumulative record times without resetting the timer
  • :iterative record times with resetting the timer
  • :total record a time only at the end of an algorithm (see stop_solver!)

The default is :cumulative, and any non-listed symbol default to using this mode.


RecordTime(; mode::Symbol=:cumulative)
RecordWhenActive <: RecordAction

record action that only records if the active boolean is set to true. This can be set from outside and is for example triggered by |RecordEvery](@ref) on recordings of the subsolver. While this is for subsolvers maybe not completely necessary, recording vlaues that are never accessible, is not that useful.


  • active: a boolean that can (de-)activated from outside to turn on/off debug
  • always_update: whether or not to call the inner debugs with nonpositive iterates (init/reset)


RecordWhenActive(r::RecordAction, active=true, always_update=true)

Access functions

getindex(r::RecordGroup, s::Symbol)
getindex(r::RecordGroup, sT::NTuple{N,Symbol})
getindex(r::RecordGroup, i)

return an array of recorded values with respect to the s, the symbols from the tuple sT or the index i. See get_record for details.

get_index(rs::RecordSolverState, s::Symbol)

Get the recorded values for recorded type s, see get_record for details.

get_index(rs::RecordSolverState, s::Symbol, i...)
ro[s, i...]

Access the recording type of type s and call its RecordAction with [i...].

get_record(s::AbstractManoptSolverState, [,symbol=:Iteration])
get_record(s::RecordSolverState, [,symbol=:Iteration])

return the recorded values from within the RecordSolverState s that where recorded with respect to the Symbol symbol as an Array. The default refers to any recordings during an :Iteration.

When called with arbitrary AbstractManoptSolverState, this method looks for the RecordSolverState decorator and calls get_record on the decorator.


return an array of tuples, where each tuple is a recorded set per iteration or record call.

get_record(r::RecordGruop, i::Int)

return an array of values corresponding to the ith entry in this record group

get_record(r::RecordGruop, s::Symbol)

return an array of recorded values with respect to the s, see RecordGroup.

get_record(r::RecordGroup, s1::Symbol, s2::Symbol,...)

return an array of tuples, where each tuple is a recorded set corresponding to the symbols s1, s2,... per iteration / record call.

set_manopt_parameter!(ams::REcordSolverState, ::Val{:Record}, args...)

Set certain values specified by args... into the elements of the recordDictionary


Internal factory functions

RecordActionFactory(s::AbstractManoptSolverState, a)

create a RecordAction where

  • a RecordAction is passed through
  • a [Symbol] creates
    • :Change to record the change of the iterates in o.x`
    • :Iterate to record the iterate
    • :Iteration to record the current iteration number
    • :Cost to record the current cost function value
    • :Time to record the total time taken after every iteration
    • :IterativeTime to record the times taken for each iteration.

and every other symbol is passed to RecordEntry, which results in recording the field of the state with the symbol indicating the field of the solver to record.

RecordActionFactory(s::AbstractManoptSolverState, t::Tuple{Symbol, T}) where {T}

create a RecordAction where

  • (:Subsolver, s) creates a RecordSubsolver with record= set to the second tuple entry

For other symbol the second entry is ignored and the symbol is used to generate a RecordEntry recording the field with the name symbol of s.

RecordFactory(s::AbstractManoptSolverState, a)

Generate a dictionary of RecordActions.

First all Symbols String, RecordActions and numbers are collected, excluding :Stop and :WhenActive. This collected vector is added to the :Iteration => [...] pair. :Stop is added as :StoppingCriterion to the :Stop => [...] pair. If any of these two pairs does not exist, it is pairs are created when adding the corresponding symbols

For each Pair of a Symbol and a Vector, the RecordGroupFactory is called for the Vector and the result is added to the debug dictonaries entry with said symbold. This is wrapped into the RecordWhenActive, when the :WhenActive symbol is present

Return value

A dictionary for the different enrty points where debug can happen, each containing a RecordAction to call.

Note that upon the initialisation all dictionaries but the :StartAlgorithm one are called with an i=0 for reset.

RecordGroupFactory(s::AbstractManoptSolverState, a)

Generate a [RecordGroup] of RecordActions. The following rules are used

  1. Any Symbol contained in a is passed to RecordActionFactory
  2. Any RecordAction is included as is.

Any Pair of a Recordaction and a symbol, that is in order RecordCost() => :A is handled, that the corresponding record action can later be accessed as g[:A], where gis the record group generated here.

If this results in more than one RecordAction a RecordGroup of these is build.

If any integers are present, the last of these is used to wrap the group in a RecordEvery(k).

If :WhenActive is present, the resulting Action is wrappedn in RecordWhenActive, making it deactivatable by its parent solver.


Further specific RecordActions can be found when specific types of AbstractManoptSolverState define them on their corresponding site.

Technical details

initialize_solver!(ams::AbstractManoptProblem, rss::RecordSolverState)

Extend the initialization of the solver by a hook to run records that were added to the :Start entry.

step_solver!(amp::AbstractManoptProblem, rss::RecordSolverState, i)

Extend the ith step of the solver by a hook to run records, that were added to the :Iteration entry.

stop_solver!(amp::AbstractManoptProblem, rss::RecordSolverState, i)

Extend the call to the stopping criterion by a hook to run records, that were added to the :Stop entry.