Skip to content

Commit ed6f8c0

Browse files
Print debug also in initial iteration (#552)
* print debug for feasibility and gradient norm in first iteration * add Changelog entry * add ´at_init´ consistently * fix tests * stepsize * doc strings * set at_init = false for DebugIterate --------- Co-authored-by: Ronny Bergmann <git@ronnybergmann.net>
1 parent 0ab9a4b commit ed6f8c0

7 files changed

Lines changed: 79 additions & 52 deletions

File tree

Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The file was started with Version `0.4`.
66
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [Unreleased]
10+
11+
### Added
12+
13+
* add keyword argument `at_init` to some debug options to control whether they print already at the initialisation and hence before the first iteration (#552)
14+
915
## [0.5.29] November 26, 2025
1016

1117
### Added

src/plans/debug.jl

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Whether internal variables are updates is determined by `always_update`.
166166
This method does not perform any print itself but relies on it's children's print.
167167
168168
It also sets the sub solvers active parameter, see |`DebugWhenActive`}(#ref).
169-
Here, the `activattion_offset` can be used to specify whether it refers to _this_ iteration,
169+
Here, the `activation_offset` can be used to specify whether it refers to _this_ iteration,
170170
the `i`th, when this call is _before_ the iteration, then the offset should be 0,
171171
for the _next_ iteration, that is if this is called _after_ an iteration, it has to be set to 1.
172172
Since usual debug is happening after the iteration, 1 is the default.
@@ -346,22 +346,25 @@ print the current cost function value, see [`get_cost`](@ref).
346346
* `format="\$prefix %f"`: format to print the output
347347
* `io=stdout`: default stream to print the debug to.
348348
* `long=false`: short form to set the format to `f(x):` (default) or `current cost: ` and the cost
349+
* `at_init=true`: whether to print also at initialization
349350
"""
350351
mutable struct DebugCost <: DebugAction
351352
io::IO
352353
format::String
354+
at_init::Bool
353355
function DebugCost(;
354-
long::Bool = false, io::IO = stdout, format = long ? "current cost: %f" : "f(x): %f"
356+
long::Bool = false, io::IO = stdout, format = long ? "current cost: %f" : "f(x): %f",
357+
at_init::Bool = true,
355358
)
356-
return new(io, format)
359+
return new(io, format, at_init)
357360
end
358361
end
359362
function (d::DebugCost)(p::AbstractManoptProblem, st::AbstractManoptSolverState, k::Int)
360-
(k >= 0) && Printf.format(d.io, Printf.Format(d.format), get_cost(p, get_iterate(st)))
363+
(k >= (d.at_init ? 0 : 1)) && Printf.format(d.io, Printf.Format(d.format), get_cost(p, get_iterate(st)))
361364
return nothing
362365
end
363366
function show(io::IO, di::DebugCost)
364-
return print(io, "DebugCost(; format=\"$(escape_string(di.format))\")")
367+
return print(io, "DebugCost(; format=\"$(escape_string(di.format))\", at_init=$(di.at_init))")
365368
end
366369
status_summary(di::DebugCost) = "(:Cost, \"$(escape_string(di.format))\")"
367370

@@ -371,22 +374,23 @@ status_summary(di::DebugCost) = "(:Cost, \"$(escape_string(di.format))\")"
371374
print a small divider (default `" | "`).
372375
373376
# Constructor
374-
DebugDivider(div,print)
377+
DebugDivider(div, io=stdout, at_init=true)
375378
376379
"""
377380
mutable struct DebugDivider{TypeIO <: IO} <: DebugAction
378381
io::TypeIO
379382
divider::String
380-
DebugDivider(divider = " | "; io::IO = stdout) = new{typeof(io)}(io, divider)
383+
at_init::Bool
384+
DebugDivider(divider = " | "; io::IO = stdout, at_init::Bool = true) = new{typeof(io)}(io, divider, at_init)
381385
end
382386
function (d::DebugDivider)(::AbstractManoptProblem, ::AbstractManoptSolverState, k::Int)
383-
if k >= 0 && !isempty(d.divider)
387+
if k >= (d.at_init ? 0 : 1) && !isempty(d.divider)
384388
print(d.io, d.divider)
385389
end
386390
return nothing
387391
end
388392
function show(io::IO, di::DebugDivider)
389-
return print(io, "DebugDivider(; divider=\"$(escape_string(di.divider))\")")
393+
return print(io, "DebugDivider(; divider=\"$(escape_string(di.divider))\", at_init=$(di.at_init))")
390394
end
391395
status_summary(di::DebugDivider) = "\"$(escape_string(di.divider))\""
392396

@@ -399,26 +403,27 @@ how to print the entry.
399403
# Additional fields
400404
401405
* `field`: symbol the entry can be accessed with within [`AbstractManoptSolverState`](@ref)
406+
* `at_init`: whether to print also at initialization
402407
403408
# Constructor
404409
405-
DebugEntry(f; prefix="\$f:", format = "\$prefix %s", io=stdout)
406-
410+
DebugEntry(f; prefix="\$f:", format = "\$prefix %s", io=stdout, at_init=true)
407411
"""
408412
mutable struct DebugEntry <: DebugAction
409413
io::IO
410414
format::String
411415
field::Symbol
412-
function DebugEntry(f::Symbol; prefix = "$f:", format = "$prefix %s", io::IO = stdout)
413-
return new(io, format, f)
416+
at_init::Bool
417+
function DebugEntry(f::Symbol; prefix = "$f:", format = "$prefix %s", io::IO = stdout, at_init::Bool = true)
418+
return new(io, format, f, at_init)
414419
end
415420
end
416421
function (d::DebugEntry)(::AbstractManoptProblem, st::AbstractManoptSolverState, k)
417-
(k >= 0) && Printf.format(d.io, Printf.Format(d.format), getfield(st, d.field))
422+
(k >= (d.at_init ? 0 : 1)) && Printf.format(d.io, Printf.Format(d.format), getfield(st, d.field))
418423
return nothing
419424
end
420425
function show(io::IO, di::DebugEntry)
421-
return print(io, "DebugEntry(:$(di.field); format=\"$(escape_string(di.format))\")")
426+
return print(io, "DebugEntry(:$(di.field); format=\"$(escape_string(di.format))\", at_init=$(di.at_init))")
422427
end
423428

424429
"""
@@ -429,6 +434,7 @@ Display information about the feasibility of the current iterate
429434
# Fields
430435
* `format`: a vector of symbols and string formatting the output
431436
* `io`: default stream to print the debug to.
437+
* `at_init`: whether to print also at initialization
432438
433439
The following symbols are filled with values
434440
@@ -449,15 +455,17 @@ format to print the output.
449455
DebugFeasibility(
450456
format=["feasible: ", :Feasible];
451457
io::IO=stdout,
458+
at_init::Bool=true,
452459
)
453460
454461
"""
455462
mutable struct DebugFeasibility <: DebugAction
456463
format::Vector{Union{String, Symbol}}
457464
io::IO
458-
function DebugFeasibility(format = ["feasible: ", :Feasible]; io::IO = stdout, atol = NaN)
465+
at_init::Bool
466+
function DebugFeasibility(format = ["feasible: ", :Feasible]; io::IO = stdout, atol = NaN, at_init::Bool = true)
459467
isnan(atol) || (@warn "Providing atol= directly to DebugFeasibility is deprecated. Use the keyword for the ConstrainedObjective instead. The value provided here ($(atol)) is ignored")
460-
return new(format, io)
468+
return new(format, io, at_init)
461469
end
462470
end
463471
function (d::DebugFeasibility)(
@@ -485,12 +493,12 @@ function (d::DebugFeasibility)(
485493
(f === :TotalEq) && (s *= "$(sum(abs.(eqc_nz); init = 0.0))")
486494
(f === :TotalInEq) && (s *= "$(sum(ineqc_pos; init = 0.0))")
487495
end
488-
print(d.io, (k > 0) ? s : "")
496+
print(d.io, (k >= (d.at_init ? 0 : 1)) ? s : "")
489497
return nothing
490498
end
491499
function show(io::IO, d::DebugFeasibility)
492500
sf = "[" * (join([e isa String ? "\"$e\"" : ":$e" for e in d.format], ", ")) * "]"
493-
return print(io, "DebugFeasibility($sf)")
501+
return print(io, "DebugFeasibility($sf, at_init=$(d.at_init))")
494502
end
495503
function status_summary(d::DebugFeasibility)
496504
sf = "[" * (join([e isa String ? "\"$e\"" : ":$e" for e in d.format], ", ")) * "]"
@@ -514,10 +522,11 @@ That way you can print the value in this case as well.
514522
* `msg`: if the `check` fails, this message is displayed
515523
* `type`: symbol specifying the type of display, possible values `:print`, `: warn`, `:info`, `:error`,
516524
where `:print` prints to `io`.
525+
* `at_init`: whether to print also at initialization
517526
518527
# Constructor
519528
520-
DebugIfEntry(field, check=(>(0)); type=:warn, message=":\$f is nonnegative", io=stdout)
529+
DebugIfEntry(field, check=(>(0)); type=:warn, message=":\$f is nonnegative", io=stdout, at_init=true)
521530
522531
"""
523532
mutable struct DebugIfEntry{F} <: DebugAction
@@ -526,14 +535,15 @@ mutable struct DebugIfEntry{F} <: DebugAction
526535
field::Symbol
527536
msg::String
528537
type::Symbol
538+
at_init::Bool
529539
function DebugIfEntry(
530-
f::Symbol, check::F = (>(0)); type = :warn, message = ":\$f nonpositive.", io::IO = stdout
540+
f::Symbol, check::F = (>(0)); type = :warn, message = ":\$f nonpositive.", io::IO = stdout, at_init::Bool = true
531541
) where {F}
532-
return new{F}(io, check, f, message, type)
542+
return new{F}(io, check, f, message, type, at_init)
533543
end
534544
end
535545
function (d::DebugIfEntry)(::AbstractManoptProblem, st::AbstractManoptSolverState, k)
536-
if (k >= 0) && (!d.check(getfield(st, d.field)))
546+
if (k >= (d.at_init ? 0 : 1)) && (!d.check(getfield(st, d.field)))
537547
format = Printf.Format(d.msg)
538548
msg = !('%' d.msg) ? d.msg : Printf.format(format, getfield(st, d.field))
539549
d.type === :warn && (@warn "$(msg)")
@@ -544,7 +554,7 @@ function (d::DebugIfEntry)(::AbstractManoptProblem, st::AbstractManoptSolverStat
544554
return nothing
545555
end
546556
function show(io::IO, di::DebugIfEntry)
547-
return print(io, "DebugIfEntry(:$(di.field), $(di.check); type=:$(di.type))")
557+
return print(io, "DebugIfEntry(:$(di.field), $(di.check); type=:$(di.type), at_init=$(di.at_init))")
548558
end
549559

550560
@doc """
@@ -694,25 +704,28 @@ debug for the current iterate (stored in `get_iterate(o)`).
694704
* `format="\$prefix %s"`: format how to print the current iterate
695705
* `long=false`: whether to have a long (`"current iterate:"`) or a short (`"p:"`) prefix default
696706
* `prefix`: (see `long` for default) set a prefix to be printed before the iterate
707+
* `at_init=true`: whether to print also at initialization
697708
"""
698709
mutable struct DebugIterate <: DebugAction
699710
io::IO
700711
format::String
712+
at_init::Bool
701713
function DebugIterate(;
702714
io::IO = stdout,
703715
long::Bool = false,
704716
prefix = long ? "current iterate:" : "p:",
705717
format = "$prefix %s",
718+
at_init::Bool = false,
706719
)
707-
return new(io, format)
720+
return new(io, format, at_init)
708721
end
709722
end
710723
function (d::DebugIterate)(::AbstractManoptProblem, st::AbstractManoptSolverState, k::Int)
711-
(k > 0) && Printf.format(d.io, Printf.Format(d.format), get_iterate(st))
724+
(k >= (d.at_init ? 0 : 1)) && Printf.format(d.io, Printf.Format(d.format), get_iterate(st))
712725
return nothing
713726
end
714727
function show(io::IO, di::DebugIterate)
715-
return print(io, "DebugIterate(; format=\"$(escape_string(di.format))\")")
728+
return print(io, "DebugIterate(; format=\"$(escape_string(di.format))\", at_init=$(di.at_init))")
716729
end
717730
status_summary(di::DebugIterate) = "(:Iterate, \"$(escape_string(di.format))\")"
718731

@@ -1081,7 +1094,7 @@ It can also be set to `:No` to deactivate the warning, but this makes this Actio
10811094
All other symbols are handled as if they were `:Always:`
10821095
10831096
# Example
1084-
DebugWaranIfFieldNotFinite(:Gradient)
1097+
DebugWarnIfFieldNotFinite(:Gradient)
10851098
10861099
Creates a [`DebugAction`] to track whether the gradient does not get `Nan` or `Inf`.
10871100
"""
@@ -1140,7 +1153,7 @@ It can also be set to `:No` to deactivate the warning, but this makes this Actio
11401153
All other symbols are handled as if they were `:Always:`
11411154
11421155
# Example
1143-
DebugWaranIfFieldNotFinite(:Gradient)
1156+
DebugWarnIfFieldNotFinite(:Gradient)
11441157
11451158
Creates a [`DebugAction`] to track whether the gradient does not get `Nan` or `Inf`.
11461159
"""
@@ -1207,7 +1220,6 @@ function (d::DebugWarnIfStepsizeCollapsed)(
12071220
amp::AbstractManoptProblem, st::AbstractManoptSolverState, k::Int
12081221
)
12091222
(k < 1) && (return nothing)
1210-
s = st.stepsize
12111223
if d.status !== :No
12121224
if get_last_stepsize(amp, st, k) d.stop_when_stepsize_less
12131225
@warn "Backtracking stopped because the stepsize fell below the threshold $(d.stop_when_stepsize_less)."
@@ -1377,6 +1389,7 @@ Note that the Shortcut symbols should all start with a capital letter.
13771389
13781390
* `:Cost` creates a [`DebugCost`](@ref)
13791391
* `:Change` creates a [`DebugChange`](@ref)
1392+
* `:Feasibility` creates a [`DebugFeasibility`](@ref)
13801393
* `:Gradient` creates a [`DebugGradient`](@ref)
13811394
* `:GradientChange` creates a [`DebugGradientChange`](@ref)
13821395
* `:GradientNorm` creates a [`DebugGradientNorm`](@ref)

src/plans/first_order_plan.jl

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,30 +1049,32 @@ abstract type AbstractRestartCondition end
10491049
debug for the gradient evaluated at the current iterate
10501050
10511051
# Constructors
1052-
DebugGradient(; long=false, prefix= , format= "\$prefix%s", io=stdout)
1052+
DebugGradient(; long=false, prefix= , format= "\$prefix%s", io=stdout, at_init=false)
10531053
10541054
display the short (`false`) or long (`true`) default text for the gradient,
10551055
or set the `prefix` manually. Alternatively the complete format can be set.
10561056
"""
10571057
mutable struct DebugGradient <: DebugAction
10581058
io::IO
10591059
format::String
1060+
at_init::Bool
10601061
function DebugGradient(;
10611062
long::Bool = false,
10621063
prefix = long ? "Gradient: " : "grad f(p):",
10631064
format = "$prefix%s",
10641065
io::IO = stdout,
1066+
at_init::Bool = false,
10651067
)
1066-
return new(io, format)
1068+
return new(io, format, at_init)
10671069
end
10681070
end
10691071
function (d::DebugGradient)(::AbstractManoptProblem, s::AbstractManoptSolverState, k::Int)
1070-
(k < 1) && return nothing
1072+
(k < (d.at_init ? 0 : 1)) && return nothing
10711073
Printf.format(d.io, Printf.Format(d.format), get_gradient(s))
10721074
return nothing
10731075
end
10741076
function show(io::IO, dg::DebugGradient)
1075-
return print(io, "DebugGradient(; format=\"$(dg.format)\")")
1077+
return print(io, "DebugGradient(; format=\"$(dg.format)\", at_init=$(dg.at_init))")
10761078
end
10771079
status_summary(dg::DebugGradient) = "(:Gradient, \"$(dg.format)\")"
10781080

@@ -1082,7 +1084,7 @@ status_summary(dg::DebugGradient) = "(:Gradient, \"$(dg.format)\")"
10821084
debug for gradient evaluated at the current iterate.
10831085
10841086
# Constructors
1085-
DebugGradientNorm([long=false,p=print])
1087+
DebugGradientNorm([long=false, format= "\$prefix%s", io=stdout, at_init=true])
10861088
10871089
display the short (`false`) or long (`true`) default text for the gradient norm.
10881090
@@ -1093,19 +1095,21 @@ display the a `prefix` in front of the gradient norm.
10931095
mutable struct DebugGradientNorm <: DebugAction
10941096
io::IO
10951097
format::String
1098+
at_init::Bool
10961099
function DebugGradientNorm(;
10971100
long::Bool = false,
10981101
prefix = long ? "Norm of the Gradient: " : "|grad f(p)|:",
10991102
format = "$prefix%s",
11001103
io::IO = stdout,
1104+
at_init::Bool = true,
11011105
)
1102-
return new(io, format)
1106+
return new(io, format, at_init)
11031107
end
11041108
end
11051109
function (d::DebugGradientNorm)(
11061110
mp::AbstractManoptProblem, s::AbstractManoptSolverState, k::Int
11071111
)
1108-
(k < 1) && return nothing
1112+
(k < (d.at_init ? 0 : 1)) && return nothing
11091113
Printf.format(
11101114
d.io,
11111115
Printf.Format(d.format),
@@ -1114,7 +1118,7 @@ function (d::DebugGradientNorm)(
11141118
return nothing
11151119
end
11161120
function show(io::IO, dgn::DebugGradientNorm)
1117-
return print(io, "DebugGradientNorm(; format=\"$(dgn.format)\")")
1121+
return print(io, "DebugGradientNorm(; format=\"$(dgn.format)\", at_init=$(dgn.at_init))")
11181122
end
11191123
status_summary(dgn::DebugGradientNorm) = "(:GradientNorm, \"$(dgn.format)\")"
11201124

@@ -1124,31 +1128,33 @@ status_summary(dgn::DebugGradientNorm) = "(:GradientNorm, \"$(dgn.format)\")"
11241128
debug for the current step size.
11251129
11261130
# Constructors
1127-
DebugStepsize(;long=false,prefix="step size:", format="\$prefix%s", io=stdout)
1131+
DebugStepsize(;long=false,prefix="step size:", format="\$prefix%s", io=stdout, at_init=true)
11281132
11291133
display the a `prefix` in front of the step size.
11301134
"""
11311135
mutable struct DebugStepsize <: DebugAction
11321136
io::IO
11331137
format::String
1138+
at_init::Bool
11341139
function DebugStepsize(;
11351140
long::Bool = false,
11361141
io::IO = stdout,
11371142
prefix = long ? "step size:" : "s:",
11381143
format = "$prefix%s",
1144+
at_init::Bool = true,
11391145
)
1140-
return new(io, format)
1146+
return new(io, format, at_init)
11411147
end
11421148
end
11431149
function (d::DebugStepsize)(
11441150
p::P, s::O, k::Int
11451151
) where {P <: AbstractManoptProblem, O <: AbstractGradientSolverState}
1146-
(k < 1) && return nothing
1152+
(k < (d.at_init ? 0 : 1)) && return nothing
11471153
Printf.format(d.io, Printf.Format(d.format), get_last_stepsize(p, s, k))
11481154
return nothing
11491155
end
11501156
function show(io::IO, ds::DebugStepsize)
1151-
return print(io, "DebugStepsize(; format=\"$(ds.format)\")")
1157+
return print(io, "DebugStepsize(; format=\"$(ds.format)\", at_init=$(ds.at_init))")
11521158
end
11531159
status_summary(ds::DebugStepsize) = "(:Stepsize, \"$(ds.format)\")"
11541160
#

0 commit comments

Comments
 (0)