Skip to content

Refactor tests: R2N#271

Open
MaxenceGollier wants to merge 12 commits intoJuliaSmoothOptimizers:masterfrom
MaxenceGollier:update-test
Open

Refactor tests: R2N#271
MaxenceGollier wants to merge 12 commits intoJuliaSmoothOptimizers:masterfrom
MaxenceGollier:update-test

Conversation

@MaxenceGollier
Copy link
Collaborator

@dpo @MohamedLaghdafHABIBOULLAH, related to #130.

This is very much WIP, but you can already take a look.
I did a proof of concept with R2N, all solvers will eventually have similar tests.

  • There should be one function that does most of the test, this function is test-solver.jl
  • each solver should have its own test file
  • allocations test don't need to be separated from the other tests
  • Just testing if the algorithm completes on the bpdn problem is not sufficient
  • having verbose > 0 adds too much information/noise into the test results

@MaxenceGollier
Copy link
Collaborator Author

LSR1 is allocating, will try to fix in LinearOperators.jl

@MohamedLaghdafHABIBOULLAH
Copy link
Collaborator

Thank you @MaxenceGollier very thoughtful, having the algorithms tested on different problems (toys, bpdn and others) will be very useful!

@codecov
Copy link

codecov bot commented Jan 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.72%. Comparing base (e0f214d) to head (b29b832).
⚠️ Report is 264 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master     #271       +/-   ##
===========================================
+ Coverage   61.53%   84.72%   +23.19%     
===========================================
  Files          11       13        +2     
  Lines        1292     1624      +332     
===========================================
+ Hits          795     1376      +581     
+ Misses        497      248      -249     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MaxenceGollier MaxenceGollier marked this pull request as ready for review January 3, 2026 10:39
@MaxenceGollier
Copy link
Collaborator Author

@dpo, @MohamedLaghdafHABIBOULLAH,

maybe we merge this first and then do the other solvers ? Or do I just do everything at once here ?
Let me know.

@MohamedLaghdafHABIBOULLAH
Copy link
Collaborator

@MaxenceGollier I think you should keep this PR tested only with R2N, so that it is easy to review and we will add the other solvers in different PR!

# Remove the x0 entry from solver_kwargs
optimized_solver_kwargs = Base.structdiff(solver_kwargs, NamedTuple{(:x0,)})
solve!(solver, reg_nlp, stats_optimized; x = x0, optimized_solver_kwargs...) # It would be interesting to check for allocations here as well but depending on
# the structure of solver_kwargs, some variables might get boxed, resulting in
Copy link
Collaborator

Choose a reason for hiding this comment

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

could you open an issue for this comment?

Copy link
Collaborator Author

@MaxenceGollier MaxenceGollier Jan 5, 2026

Choose a reason for hiding this comment

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

I think it is just a bad idea to add the allocations tests inside this function because this means that you can not assign a variable to one of the solver_constructor_kwargs or solver_kwargs, or else they will get boxed and result in allocations.

To be clearer @wrappedallocs works in such a way that even if

@wrappedallocs solve!(solver, reg_nlp, stats; atol = 1e-3)

returns 0,

tol = 1e-3
@wrappedallocs solve!(solver, reg_nlp, stats; atol = tol)

doesn't.

I think adding this constraint is very confusing so we should just leave the allocation tests out of this function.
I want to keep the comment though so that users will understand why we did not add the allocation tests there, but it might be more precise.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we have a function wrappedallocsv2 to avoid this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, there is nothing "easy" we can do, keyword arguments will get boxed, this is a Julia issue.

solve!(solver, reg_nlp, stats, σk = 1.0, β = 1e16, atol = 1e-6, rtol = 1e-6)
) == 0

#test_solver(reg_nlp, # FIXME: divide by 0 error in the LBFGS approximation
Copy link
Collaborator

@MohamedLaghdafHABIBOULLAH MohamedLaghdafHABIBOULLAH Jan 4, 2026

Choose a reason for hiding this comment

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

@MaxenceGollier
I checked this in detail. The issue is actually related to R2DH, and we should probably open a dedicated issue. The problem occurs when $$s = 0$$. In this case, R2DH does not stop because $$\xi = h_k - m_k(s) + \max(1, |h_k|)\times 10 \times \varepsilon = \max(1, |h_k|)\times 10 \times \varepsilon.$$
Although this quantity may be small, it is not negligible. When we compute its root, the resulting value can be larger than expected, which may prevent the algorithm from terminating.

A simple and reasonable fix is to assume instead that $$\xi = h_k - m_k(s).$$

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I never understood why we added this term to $$\xi$$ @dpo ?

Copy link
Member

Choose a reason for hiding this comment

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

That term is there to compensate for catastrophic cancellation between $h_k$ and $m_k(s)$. It’s hard to believe that a solver would compute $s = 0$ exactly. That suggests that the solver should have stopped earlier.

@MohamedLaghdafHABIBOULLAH Could you please show the run of R2DH where you observed $s = 0$?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@MohamedLaghdafHABIBOULLAH, I agree with Dominique, could you provide us a run where this occurs ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry I just saw the message, yes of course.

@MaxenceGollier MaxenceGollier changed the title [WIP] Refactor Tests Refactor tests: R2N Jan 5, 2026
Copy link
Member

@dpo dpo left a comment

Choose a reason for hiding this comment

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

This looks great, thank you! I agree that we can have this PR focus on R2N only, and open future PRs for other solvers.

@@ -0,0 +1,48 @@
function test_solver(
reg_nlp::R,
solver_name::String;
Copy link
Member

Choose a reason for hiding this comment

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

Just pass the function name instead of a string.

solver_fun = getfield(RegularizedOptimization, Symbol(solver_name))
stats_basic = solver_fun(
reg_nlp.model,
reg_nlp.h,
Copy link
Member

Choose a reason for hiding this comment

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

You should be able to just pass reg_nlp.


# Remove the x0 entry from solver_kwargs
optimized_solver_kwargs = Base.structdiff(solver_kwargs, NamedTuple{(:x0,)})
solve!(solver, reg_nlp, stats_optimized; x = x0, optimized_solver_kwargs...) # It would be interesting to check for allocations here as well but depending on
Copy link
Member

Choose a reason for hiding this comment

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

Testing for allocations would be the main point I think, because the first set of test (where you call solver_fun) already call solver_contructor and solve!(). I wonder if something like @wrappedallocs @eval solve!(solver, reg_nlp, stats; atol = $atol) would work. I agree that allocation tests need not be in this function, but, in general, it should be possible to construct the expression of the solve call and measure allocations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants