Skip to content

WIP: Fix for itkGradientDescentOptimizerv4#2572

Closed
gregsharp wants to merge 1 commit intoInsightSoftwareConsortium:mainfrom
gregsharp:issue-2570-gradient-descent-v4
Closed

WIP: Fix for itkGradientDescentOptimizerv4#2572
gregsharp wants to merge 1 commit intoInsightSoftwareConsortium:mainfrom
gregsharp:issue-2570-gradient-descent-v4

Conversation

@gregsharp
Copy link
Copy Markdown

@gregsharp gregsharp commented Jun 3, 2021

Issue 2570.
AdvanceOneStep() is now called prior to GetValueAndDerivative() rather than afterward.
Tests in ITKOptimizersv4TestDriver have been updated to match the algorithmic change.

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Refer to the ITK Software Guide for
further development details if necessary.

Issue 2570.
AdvanceOneStep() is now called prior to GetValueAndDerivative() rather than afterward.
Tests in ITKOptimizersv4TestDriver have been updated to match the algorithmic change.
@github-actions github-actions Bot added area:Numerics Issues affecting the Numerics module type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct labels Jun 3, 2021
Copy link
Copy Markdown
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments in-line

// Verify that the optimizer stays at the initial position if
// number of iterations is set to one.
std::cout << "\nCheck the optimizer position (parameters) are equal to the initial "
"value when number of iterations is set to one:"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be the case for 0 iterations? One iteration should update the parameters once?

initialPosition[1] = -100;
metric->SetParameters(initialPosition);

itkOptimizer->SetNumberOfIterations(1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also zero

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing behavior is:

Zero iterations: No function evaluations performed
One iteration: One function evaluation, update position (Parameters)

The new behavior is:

Zero iterations: No function evaluations performed
One iteration: One function evaluation, do not update position (Parameters)

So it depends upon what is the definition of an iteration. It would be helpful to have documentation for what is supposed to happen.

Copy link
Copy Markdown
Member

@jhlegarreta jhlegarreta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing thins @gregsharp. I have not experienced (consciously) the issue and have not tried the fix, but I trust it is much needed 💯. Thus my review is just about some superficial/stylistic aspects:

  • Commit messages should adhere to the required style. Among others:
    • A short subject/heading descriptive of the change (what does this fix?). Reading Fix for itkGradientDescentOptimizerv4 does not allow us to say what this fixes. Think that your commit message and PR will be read many, many times, and having a clear, self-contained body of explanation about the change is invaluable.
    • The linked Chris Beam's post on how to write good commit messages is an invaluable guide.
    • Use the body of the message to briefly re-state the purpose of the PR and what it fixes: your description and expected and actual behavior passages in the issue are enlightening about it I'd say, and invaluable for anyone visiting the commit history (and this last one happens frequently). After that you can add what you have:
  `AdvanceOneStep()` is now called prior to `GetValueAndDerivative()` rather than afterward (add an explanation if the above is not enough).

   Update related tests in the v4 Optimizers framework to match the algorithmic change.

Note that the Drivers (or TestDrivers) are just a mechanism of the testing framework, not the relevant name of the module.

  • If this PR closes the cross-referenced issue, it can automatically be closed when this PR gets merged using any of the appropriate GitHub keys.
  • Please check the appropriate checkboxes in the PR checklist. It allows maintainers and those requiring to see the history to know a little bit more about the changes. I'd say that the first four items apply to your PR.
  • Please remove the PR's HTML markup instructions (if you feel like it should be written, a PR would be welcome 💯).

Another inline comment.

Additionally, and more related to the discourse post,

  • As for the the fix due to the need to fork the repository and style formatting ([comment in discourse](
    Anorther inline comment.)) due to omissions or assumptions in CONTRIBUTING.md, improvements under the form of PRs to it are more than welcome !
    I ignore if that may have been related to not having executed SetupForDevelopment.sh or this one not having worked. If that is the case, a fix would be welcome !
    If the style is not auto-formatted locally, the clang-format linter action will flag the style inconsistencies; and in therory adding the action:ApplyClangFormat in a PR comment should auto-format the code, but there seems to be some issue with the GitHub permissions (?) so that the auto-formatting can take place automatically by the action.
  • Brad's comments in the discourse thread (here and here)in the cross-referenced issue about the lack of better testing might be worthwhile considering, or be opened as issues so that they can be tracked, and hopefully addressed at some point.

Thanks for all the hard work in fixing this 💯.

catch (const itk::ExceptionObject & e)
{
return EXIT_FAILURE;
}
Copy link
Copy Markdown
Member

@jhlegarreta jhlegarreta Jun 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above try/catch block can be shortened using ITK_TRY_EXPECT_NO_EXCEPTION(itkOptimizer->StartOptimization());. It is preferred for the sake of compactness and to avoid boilerplate code.

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Jun 7, 2021

adding the action:ApplyClangFormat in a PR comment should auto-format the code

I think this only works for PRs made from branches within ITK repo, which is not the way PRs are normally made in ITK.

@gregsharp
Copy link
Copy Markdown
Author

gregsharp commented Jun 7, 2021

Commit messages should adhere to the required style. Among others:

Sure. If the PR contains multiple commits, should I squash the work into a single commit and update the message at that time?

Note that the Drivers (or TestDrivers) are just a mechanism of the testing framework, not the relevant name of the module.

Sorry I don't understand.

Please remove the PR's HTML markup instructions (if you feel like it should be written, a PR would be welcome 100).

Sorry, also I don't understand, Where is the HTML markup?

Brad's comments in the discourse thread (here and here)in the cross-referenced issue about the lack of better testing might be worthwhile considering, or be opened as issues so that they can be tracked, and hopefully addressed at some point.

Even describing the current state of Optimizerv4 tests and making improvement suggestions in a ticket is a lot of work. But if you think it is useful I can try. It seems sufficiently independent of this ticket to be separate.

Regarding the code formatting, none of the code I touched matches the style guide found in the ITK Software Guide (with regards to indenting, curly brace placement). So I just kind of manually adjusted things so they look the same as the existing code.

Thank you for review!

@jhlegarreta
Copy link
Copy Markdown
Member

jhlegarreta commented Jun 7, 2021

Thanks for considering the comments @gregsharp 💯.

Sure. If the PR contains multiple commits, should I squash the work into a single commit and update the message at that time?

Depends on the commits; if they serve to different purposes (e.g. DOC, STYLE, PERF, BUG, etc.), best is to leave them as separate commits. Otherwise, we prefer squashing before the branch gets merged, or alternatively amending the commit and push-forcing as changes are done to answer the comments or fix tests that do not pass.

Sorry I don't understand.

ITKOptimizersv4TestDriver is just a command used in the CMakeLists.txt file to perform some actions on tests; it does not exist in the source code tree, so it is not meaningful from the point of the view of the ITK modules or source tree.

Sorry, also I don't understand, Where is the HTML markup?

When you open the PR, there is a body of text inside HTML comment markup <!-- --> in the PR message area. Not sure if people remove it, but I think it is desirable (just leaving the PR checkboxes that apply).

Even describing the current state of Optimizerv4 tests and making improvement suggestions in a ticket is a lot of work. But if you think it is useful I can try. It seems sufficiently independent of this ticket to be separate.

Sounds reasonable. I did not mean that it should be done within this PR. If you feel like you do have a good understanding of what should be done to improve the current state following Brad's comments, opening an issue would be best. But I understand that that requires in itself a non-negligible amount of work.

Regarding the code formatting, none of the code I touched matches the style guide found in the ITK Software Guide (with regards to indenting, curly brace placement). So I just kind of manually adjusted things so they look the same as the existing code.

The automatic style enforcing tool (i.e. clang-format, which should be installed when executing the SetupForDevelopment.sh script) should do the job for you. Although I admit that in the past I had issues with it. Also, unfortunately some of the decisions made when the tool was adopted have not made yet to the ITK SW Guide. I'm sorry for that.

Thank you for review!

👍

@stale
Copy link
Copy Markdown

stale Bot commented Apr 16, 2022

This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.

@stale stale Bot added the status:Use_Milestone_Backlog Use "Backlog" milestone instead of label for issues without a fixed deadline label Apr 16, 2022
@hjmjohnson hjmjohnson marked this pull request as draft February 15, 2026 21:16
@hjmjohnson
Copy link
Copy Markdown
Member

@gregsharp — superseded by PR #6196.

After deep-diving this draft I confirmed the bug you diagnosed in #2570 is real (a TDD regression GTest fails by ~4 score-units against a quadratic fixture on current upstream/main, passes on the fix). The new PR uses a more surgical approach than the 2021 branch — a 6-line move of InvokeEvent(IterationEvent()) from the end of AdvanceOneStep to the loop in ResumeOptimization, before the step. That preserves the existing "1 iteration = 1 step taken" semantic and sidesteps the iteration-semantics question @dzenanz raised here in 2021, which never resolved.

Your authorship is preserved via Co-Authored-By: in the new commit, and #6196 cites this PR, the original Discourse thread, and your plastimatch reproducer.

cc @dzenanz @jhlegarreta — the new PR is ready for fresh review at #6196.

Closing this draft as superseded; reopening would duplicate review work.

@hjmjohnson hjmjohnson closed this May 2, 2026
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request May 2, 2026
Issue InsightSoftwareConsortium#2570: an observer attached to IterationEvent on
GradientDescentOptimizerv4 (or its subclasses) was given an internally
inconsistent (value, position) pair. The optimizer was firing the event
at the end of AdvanceOneStep(), after UpdateTransformParameters() had
already moved the position from X to X+step, while m_CurrentMetricValue
still held v(X). Observers reading GetCurrentMetricValue() and
GetCurrentPosition() saw value-at-old-position with the new position.

Move InvokeEvent(IterationEvent()) into the loop in
GradientDescentOptimizerv4Template::ResumeOptimization(), fired BEFORE
AdvanceOneStep() so the reported (value, position) pair always agrees.
Remove the now-redundant InvokeEvent at the end of each subclass's
AdvanceOneStep override (RegularStep, QuasiNewton, GradientDescentLineSearch,
ConjugateGradientLineSearch).

Adds a GTest regression guard
(itkGradientDescentOptimizerv4ObserverGTest.cxx) that fails on the
unfixed code with a ~4-unit value/position mismatch on a quadratic
fixture, and passes on the fixed code.

Closes InsightSoftwareConsortium#2570. Supersedes the WIP draft InsightSoftwareConsortium#2572 by @gregsharp; the test
demonstrates Greg's diagnosis is correct, the fix here is a simpler
one-event-move that does not change iteration semantics.

Co-Authored-By: Gregory C. Sharp <gregsharp.geo@yahoo.com>
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request May 4, 2026
Issue InsightSoftwareConsortium#2570: an observer attached to IterationEvent on
GradientDescentOptimizerv4 (or its subclasses) was given an internally
inconsistent (value, position) pair. The optimizer was firing the event
at the end of AdvanceOneStep(), after UpdateTransformParameters() had
already moved the position from X to X+step, while m_CurrentMetricValue
still held v(X). Observers reading GetCurrentMetricValue() and
GetCurrentPosition() saw value-at-old-position with the new position.

Move InvokeEvent(IterationEvent()) into the loop in
GradientDescentOptimizerv4Template::ResumeOptimization(), fired BEFORE
AdvanceOneStep() so the reported (value, position) pair always agrees.
Remove the now-redundant InvokeEvent at the end of each subclass's
AdvanceOneStep override (RegularStep, QuasiNewton, GradientDescentLineSearch,
ConjugateGradientLineSearch).

Adds a GTest regression guard
(itkGradientDescentOptimizerv4ObserverGTest.cxx) that fails on the
unfixed code with a ~4-unit value/position mismatch on a quadratic
fixture, and passes on the fixed code.

Closes InsightSoftwareConsortium#2570. Supersedes the WIP draft InsightSoftwareConsortium#2572 by @gregsharp; the test
demonstrates Greg's diagnosis is correct, the fix here is a simpler
one-event-move that does not change iteration semantics.

Co-Authored-By: Gregory C. Sharp <gregsharp.geo@yahoo.com>
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request May 5, 2026
Issue InsightSoftwareConsortium#2570: an observer attached to IterationEvent on
GradientDescentOptimizerv4 (or its subclasses) was given an internally
inconsistent (value, position) pair. The optimizer was firing the event
at the end of AdvanceOneStep(), after UpdateTransformParameters() had
already moved the position from X to X+step, while m_CurrentMetricValue
still held v(X). Observers reading GetCurrentMetricValue() and
GetCurrentPosition() saw value-at-old-position with the new position.

Move InvokeEvent(IterationEvent()) into the loop in
GradientDescentOptimizerv4Template::ResumeOptimization(), fired BEFORE
AdvanceOneStep() so the reported (value, position) pair always agrees.
Remove the now-redundant InvokeEvent at the end of each subclass's
AdvanceOneStep override (RegularStep, QuasiNewton, GradientDescentLineSearch,
ConjugateGradientLineSearch).

Adds a GTest regression guard
(itkGradientDescentOptimizerv4ObserverGTest.cxx) that fails on the
unfixed code with a ~4-unit value/position mismatch on a quadratic
fixture, and passes on the fixed code.

Closes InsightSoftwareConsortium#2570. Supersedes the WIP draft InsightSoftwareConsortium#2572 by @gregsharp; the test
demonstrates Greg's diagnosis is correct, the fix here is a simpler
one-event-move that does not change iteration semantics.

Co-Authored-By: Gregory C. Sharp <gregsharp.geo@yahoo.com>
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request May 6, 2026
Issue InsightSoftwareConsortium#2570: an observer attached to IterationEvent on
GradientDescentOptimizerv4 (or its subclasses) was given an internally
inconsistent (value, position) pair. The optimizer was firing the event
at the end of AdvanceOneStep(), after UpdateTransformParameters() had
already moved the position from X to X+step, while m_CurrentMetricValue
still held v(X). Observers reading GetCurrentMetricValue() and
GetCurrentPosition() saw value-at-old-position with the new position.

Move InvokeEvent(IterationEvent()) into the loop in
GradientDescentOptimizerv4Template::ResumeOptimization(), fired BEFORE
AdvanceOneStep() so the reported (value, position) pair always agrees.
Remove the now-redundant InvokeEvent at the end of each subclass's
AdvanceOneStep override (RegularStep, QuasiNewton, GradientDescentLineSearch,
ConjugateGradientLineSearch).

Adds a GTest regression guard
(itkGradientDescentOptimizerv4ObserverGTest.cxx) that fails on the
unfixed code with a ~4-unit value/position mismatch on a quadratic
fixture, and passes on the fixed code.

Closes InsightSoftwareConsortium#2570. Supersedes the WIP draft InsightSoftwareConsortium#2572 by @gregsharp; the test
demonstrates Greg's diagnosis is correct, the fix here is a simpler
one-event-move that does not change iteration semantics.

Co-Authored-By: Gregory C. Sharp <gregsharp.geo@yahoo.com>
hjmjohnson added a commit that referenced this pull request May 6, 2026
BUG: Fire IterationEvent before AdvanceOneStep in v4 gradient optimizers (closes #2570, supersedes #2572)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Numerics Issues affecting the Numerics module status:Use_Milestone_Backlog Use "Backlog" milestone instead of label for issues without a fixed deadline type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants