Conversation
- Add Uno solver with CPU-only support and ADNLP modeler compatibility - Implement complete metadata with validation for all Uno options - Add comprehensive test suite covering: * Type hierarchy and contracts (test_solver_types.jl) * Extension stubs and error handling (test_extension_stubs.jl) * Parameter validation (test_uno_parameter_validation.jl) * Type stability (test_type_stability.jl) * Module exports and interface compliance (test_solvers.jl) * Integration with validation modes (test_comprehensive_validation.jl) * Real strategy mode tests (test_real_strategies_mode.jl) * Route-to functionality (test_route_to_comprehensive.jl) * Full extension tests with problem solving (test_uno_extension.jl) Key features: - Uno supports presets (ipopt, filtersqp) and full option set - CPU execution only (GPU parameter rejected at compile time) - ADNLP modeler compatibility (no Exa support) - Comprehensive option validation wi - Add Uno solver with CPU-only support and ADNLP modeler compatibility AP- Implement complete metadata with validation for all Uno options - Arc- Add comprehensive test suite covering: * Type hierarchy and l * Type hierarchy and contracts (test_oS * Extension stub
…sion - Add UnoSolver to test targets in Project.toml - Import NLPModels in CTSolversUno extension to fix precompilation error This fixes the missing dependency error when running Uno tests.
- Use raw UnoSolver constants for direct solver calls (solver(nlp)) - Use Symbol() wrapper for CommonSolve.solve calls (goes through extract_solver_infos) - Fix all status comparisons in test_uno_extension.jl This fixes 8 test failures, reducing from 53 pass/8 fail/11 error to 61 pass/0 fail/11 error.
- Add SolverCore import and status mapping function in ext/CTSolversUno.jl - Map Uno statuses to SolverCore symbols (UNO_FEASIBLE_KKT_POINT → :first_order, etc.) - Convert UnoSolver.Statistics to GenericExecutionStats in solve_with_uno - Update all tests to use GenericExecutionStats fields (.status, .solution, .objective) - Skip Initial Guess tests (Uno performs 1 iteration even with max_iterations=0) - Fix integration tests: add UnoSolver import and use correct canonical option names - All tests passing: 192 passed, 2 intentionally skipped
|
@amontoison @cvanaret Does Uno work with ExaModels? Or just ADNLPModels? |
|
@ocots Uno works with any |
| function _uno_status_to_solvercore(optimization_status::Cint, solution_status::Cint)::Symbol | ||
| if optimization_status == UnoSolver.UNO_ITERATION_LIMIT | ||
| return :max_iter | ||
| elseif optimization_status == UnoSolver.UNO_TIME_LIMIT | ||
| return :max_time | ||
| elseif optimization_status == UnoSolver.UNO_EVALUATION_ERROR | ||
| return :exception | ||
| elseif optimization_status == UnoSolver.UNO_ALGORITHMIC_ERROR | ||
| return :exception | ||
| else # UNO_SUCCESS | ||
| if solution_status == UnoSolver.UNO_FEASIBLE_KKT_POINT | ||
| return :first_order | ||
| elseif solution_status == UnoSolver.UNO_FEASIBLE_FJ_POINT | ||
| return :acceptable | ||
| elseif solution_status == UnoSolver.UNO_INFEASIBLE_STATIONARY_POINT | ||
| return :infeasible | ||
| elseif solution_status == UnoSolver.UNO_FEASIBLE_SMALL_STEP | ||
| return :small_step | ||
| elseif solution_status == UnoSolver.UNO_INFEASIBLE_SMALL_STEP | ||
| return :small_step | ||
| else # UNO_UNBOUNDED | ||
| return :unbounded | ||
| end | ||
| end | ||
| end | ||
|
|
||
| """ | ||
| $(TYPEDSIGNATURES) | ||
|
|
||
| Convert UnoSolver.Statistics to SolverCore.GenericExecutionStats. | ||
|
|
||
| This conversion allows Uno to integrate seamlessly with the CTSolvers pipeline | ||
| which expects SolverCore.AbstractExecutionStats. | ||
|
|
||
| # Arguments | ||
| - `nlp::NLPModels.AbstractNLPModel`: The NLP model (needed for GenericExecutionStats constructor) | ||
| - `uno_stats::UnoSolver.Statistics`: Uno solver execution statistics | ||
|
|
||
| # Returns | ||
| - `SolverCore.GenericExecutionStats`: Converted statistics compatible with CTSolvers | ||
|
|
||
| # Field Mapping | ||
| - `status` ← mapped from `optimization_status` and `solution_status` | ||
| - `solution` ← `primal_solution` | ||
| - `objective` ← `solution_objective` | ||
| - `dual_feas` ← `solution_stationarity` | ||
| - `primal_feas` ← `solution_primal_feasibility` | ||
| - `multipliers` ← `constraint_dual_solution` | ||
| - `multipliers_L` ← `lower_bound_dual_solution` | ||
| - `multipliers_U` ← `upper_bound_dual_solution` | ||
| - `iter` ← `number_iterations` | ||
| - `elapsed_time` ← `cpu_time` | ||
| """ | ||
| function _uno_to_generic_stats(nlp::NLPModels.AbstractNLPModel, uno_stats::UnoSolver.Statistics)::SolverCore.GenericExecutionStats | ||
| # Map Uno status to SolverCore status | ||
| status = _uno_status_to_solvercore( | ||
| uno_stats.optimization_status, | ||
| uno_stats.solution_status | ||
| ) | ||
|
|
||
| # Create GenericExecutionStats with all fields marked as reliable | ||
| stats = SolverCore.GenericExecutionStats( | ||
| nlp; | ||
| status=status, | ||
| solution=uno_stats.primal_solution, | ||
| objective=uno_stats.solution_objective, | ||
| dual_feas=uno_stats.solution_stationarity, | ||
| primal_feas=uno_stats.solution_primal_feasibility, | ||
| multipliers=uno_stats.constraint_dual_solution, | ||
| multipliers_L=uno_stats.lower_bound_dual_solution, | ||
| multipliers_U=uno_stats.upper_bound_dual_solution, | ||
| iter=Int(uno_stats.number_iterations), | ||
| elapsed_time=uno_stats.cpu_time | ||
| ) | ||
|
|
||
| return stats | ||
| end |
There was a problem hiding this comment.
@amontoison @cvanaret I have started to add Uno solver. It is easier for me to deal with SolverCore.AbstractExecutionStats hence I have made this conversion system. Do you think it could be placed directly in UnoSolver.jl?
ps: I am not sure it is correct.
There was a problem hiding this comment.
Yes, I think we should add a option to decide what is the type of the returned statistics (UnoSolver.Statistics or SolverCore.AbstractExecutionStats).
There was a problem hiding this comment.
@ocots Can you open a PR with this converter in UnoSolver.jl ?
I can't open new PR now until I have approvals for working on it.
I am fine with adding SolverCore.jl as a dependency.
I would like to use a GenericExecutionStats instead of a Statistics on the long run but we need more attributes in GenericExecutionStats before.
@cvanaret will maybe open a PR in SolverCore.jl :)
| Test.@testset "Initial Guess - max_iterations=0" begin | ||
| Test.@testset "Rosenbrock" verbose=VERBOSE showtiming=SHOWTIMING begin | ||
| Test.@testset "Modelers.ADNLP" verbose=VERBOSE showtiming=SHOWTIMING begin | ||
| Test.@test_skip "Uno performs 1 iteration even with max_iterations=0" | ||
| end | ||
| end | ||
|
|
||
| Test.@testset "Elec" verbose=VERBOSE showtiming=SHOWTIMING begin | ||
| Test.@testset "Modelers.ADNLP" verbose=VERBOSE showtiming=SHOWTIMING begin | ||
| Test.@test_skip "Uno performs 1 iteration even with max_iterations=0" | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
@amontoison @cvanaret I wanted to add some tests with 0 iterations, but it seems that when max_iterations=0, Uno performs 1 iteration.
For us, it would be useful to allow 0 iterations in order to build an optimal control solution directly from an initial guess. Indeed, the input (the initial guess) and the output (the optimal control solution) are not of the same type.
There was a problem hiding this comment.
Indeed, this special case is not detected. I can patch it real quick.
cvanaret/Uno#597
There was a problem hiding this comment.
It would be great!
Soon we will make a new release of OC with Uno and relaunch all our benchmarks 🤞
There was a problem hiding this comment.
It's not going to be that quick because I have to do Uno and Uno_jll releases first 😅
There was a problem hiding this comment.
@ocots apparently you deleted your comment. Your function _uno_status_to_solvercore is missing the test solution_status == UNO_NOT_OPTIMAL. But I did forget to set the status to UNO_ITERATION_LIMIT 😆
There was a problem hiding this comment.
@cvanaret I tried it, and it works—thanks!
However, even though the number of iterations is effectively 0, the status is not UNO_ITERATION_LIMIT but UNO STATUS: optimization_status = UNO_SUCCESS, solution_status = UNO_NOT_OPTIMAL. Is that expected?
Tested with ADNLP and Exa: https://github.com/control-toolbox/OptimalControl.jl/blob/4176281a4709af90454ee8595ce4dc3c5036ed7c/src/helpers/methods.jl#L42-L60 Some tests from https://github.com/control-toolbox/OptimalControl.jl/blob/4176281a4709af90454ee8595ce4dc3c5036ed7c/test/suite/solve/test_canonical.jl#L52-L152 (not benchmarks since no "compilation" call):
|
|
Nice! Is that with Uno-ipopt or Uno-filtersqp? Based on feedback here, I would recommend using the filtersqp preset. It nicely exploits negative curvature and is way more robust than the ipopt preset. |
It is with |

@jbcaillau @PierreMartinon @joseph-gergaud Uno solver
Key features: