Skip to content

added: pretty-print optimization problem dimensions for MPC and MHE#378

Merged
franckgaga merged 7 commits into
mainfrom
print_optim_dim
Jun 16, 2026
Merged

added: pretty-print optimization problem dimensions for MPC and MHE#378
franckgaga merged 7 commits into
mainfrom
print_optim_dim

Conversation

@franckgaga

@franckgaga franckgaga commented Jun 16, 2026

Copy link
Copy Markdown
Member

Print optimization problem dimensions

Printing a LinMPC, NonLinMPC or MovingHorizonEstimator object will now shows the dimensions of the optimization problem that is, the number of decision variables and constraints. Below is a preview of what is printed for the 3 types. The new information is in the optimization section at the end. I also moved the slack variable dimension in this section.

LinMPC object

LinMPC controller with a sample time Ts = 2.0 s:
├ estimator: MovingHorizonEstimator
├ model: LinModel
├ optimizer: OSQP 
├ transcription: SingleShooting
└ dimensions:
  │ ├ 10 prediction steps Hp
  │ ├  2 control steps Hc
  │ ├ 10 estimation steps He
  │ ├  2 manipulated inputs u (2 integrating states)
  │ ├  4 estimated states x̂
  │ ├  2 measured outputs ym (0 integrating states)
  │ ├  0 unmeasured outputs yu
  │ └  0 measured disturbances d
  └ optimization:
    ├  5 decision variables Z̃ (1 slack variable)
    ├ 11 linear inequality constraints A (0 custom)
    └  0 linear equality constraints Aeq

NonLinMPC object

NonLinMPC controller with a sample time Ts = 0.1 s:
├ estimator: UnscentedKalmanFilter
├ model: NonLinModel
├ optimizer: Ipopt 
├ transcription: SingleShooting
├ gradient: AutoForwardDiff
├ jacobian: AutoForwardDiff
├ hessian: nothing
└ dimensions:
  │ ├ 20 prediction steps Hp
  │ ├  2 control steps Hc
  │ ├  1 manipulated inputs u (1 integrating states)
  │ ├  3 estimated states x̂
  │ ├  1 measured outputs ym (0 integrating states)
  │ ├  0 unmeasured outputs yu
  │ └  0 measured disturbances d
  └ optimization:
    ├  2 decision variables Z̃ (0 slack variable)
    ├ 40 linear inequality constraints A (0 custom)
    ├  0 linear equality constraints Aeq
    ├  0 nonlinear inequality constraints g (0 custom)
    └  0 nonlinear equality constraints geq

MovingHorizonEstimator object:

MovingHorizonEstimator estimator with a sample time Ts = 4.0 s:
├ model: NonLinModel
├ optimizer: Ipopt 
├ gradient: AutoForwardDiff
├ jacobian: AutoForwardDiff
├ hessian: AutoSparse (AutoForwardDiff, TracerSparsityDetector, GreedyColoringAlgorithm)
├ arrival covariance: UnscentedKalmanFilter 
├ direct: true
└ dimensions:
  │ ├ 10 estimation steps He
  │ ├  2 manipulated inputs u (0 integrating states)
  │ ├  6 estimated states x̂
  │ ├  2 measured outputs ym (2 integrating states)
  │ ├  0 unmeasured outputs yu
  │ └  1 measured disturbances d
  └ optimization:
    ├ 66 decision variables Z̃ (0 slack variable)
    ├  1 linear inequality constraints A
    └ 10 nonlinear inequality constraints g (0 custom)

Why?

It is more explicit and transparent for the user like this. Especially knowing that the classification can look surprising at first. For example :

  • bounds on manipulated inputs $\mathbf{u}$ are always linear inequality constraints since the decision variables are the input increments $\mathbf{\Delta u}$ over $H_c$
  • even bounds on the input increments $\mathbf{\Delta u}$ are always linear inequality constraints (to support relaxation)
  • the slack variable is treated as a linear equality constraint instead of a box constraint (it was simpler like this since it would be the only box constraint, and also because some optimizers like the default OSQP does not support box constraint)
  • the stochastic state defects are always linear equality constraints, even if the plant model is a NonLinModel (because the stochastic model of the unmeasured disturbances is linear)
  • bounds on the state estimate $\mathbf{\hat{x}}$ in the MovingHorzionEstimator will always create linear inequality constraints even if the plant model is a NonLinModel (because the arrival state estimate is always a decision variable)

Some of these choices may change in the future. For example, I have a feeling that treating the slack variable $\varepsilon$ as a box constraint would help the MHE for the NLP case (this case is finicky right now).

@codecov-commenter

codecov-commenter commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.30508% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 98.58%. Comparing base (4b02782) to head (7b9d014).

Files with missing lines Patch % Lines
src/predictive_control.jl 85.71% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #378   +/-   ##
=======================================
  Coverage   98.57%   98.58%           
=======================================
  Files          27       27           
  Lines        5479     5512   +33     
=======================================
+ Hits         5401     5434   +33     
  Misses         78       78           

☔ View full report in Codecov by Harness.
📢 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.

@franckgaga franckgaga merged commit b58cf8e into main Jun 16, 2026
5 checks passed
@franckgaga franckgaga deleted the print_optim_dim branch June 16, 2026 23:41
@franckgaga

franckgaga commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

@baggepinnen

Out of curiosity, do you have any comments on these choices currently in the package :

  • even bounds on the input increments $\mathbf{Δu}$ are always linear inequality constraints (to support relaxation)
  • the slack variable is treated as a linear equality constraint instead of a box constraint (it was simpler like this since it would be the only box constraint, and also because some optimizers like the default OSQP does not support box constraint)
  • the stochastic state defects are always linear equality constraints, even if the plant model is a NonLinModel (because the stochastic model of the unmeasured disturbance is linear)
  • bounds on the state estimate $\mathbf{\hat{x}}$ in the MovingHorzionEstimator will always create linear inequality constraints even if the plant model is a NonLinModel (because the arrival state estimate is always a decision variable)

I have a hunch that treating the box constraints (lower and upper limits on a decision variable) as box constraint instead of linear equality constraints (e.g. the slack variable $\epsilon$, $\mathbf{Δu}$ bounds when they are not relaxed, etc.) would improve the performances in the case of nonlinear programming, since most NLP solver treats box constraints and all the other constraints differently. What's your take ?

@franckgaga

Copy link
Copy Markdown
Member Author

It's @cvanaret presentation at JuMP-dev 2026 that made me think about this. What's your take on this @cvanaret ? Right now I do not use any box constraints to define my NLP. They are all defined as linear inequality constraints (with identity A matrix, if it's really a box constraint) since it was easier for me. Knowing that cvanaret/Uno#474 allows efficient $b$ vector update in $Ax \le b$ constraints, do you think it's still a bad idea in terms of performances ?

@cvanaret

cvanaret commented Jun 17, 2026

Copy link
Copy Markdown

Absolutely. Since the immense majority of solvers handle bound constraints separately, you should define your model accordingly.

For some methods (like active-set/SQP methods), I don't think this will make a big difference because they handle linear constraints and bound constraints pretty much the same way. In interior-point methods though, bound constraints are handled directly while general inequality constraints get attached slack variables => the problem becomes larger (still sparse, but pointlessly larger).

Should your problem be fully bound constrained (no general constraints), it can be solved with very efficient (projection) methods. You might not want to unleash the more complex (and probably slower) active-set or interior-point methods there.

Under the solver's hood, the bound constraints most likely do not appear in the computation of constraint violation or in the merit function, aren't relaxed, etc. Uno in particular doesn't have (yet) a special treatment for linear constraints and has no presolver, so you should declare the bound constraints as such.

Knowing that cvanaret/Uno#474 allows efficient b vector update in A x ≤ b constraints, do you think it's still a bad idea in terms of performances ?

The update of the model is efficient, but Uno doesn't allow proper reoptimization yet 😅 Note that it also allows an efficient update of the bound constraints without model recreation.

@franckgaga

Copy link
Copy Markdown
Member Author

Alright, many thanks for the detailed answer. I will open a PR that treats box constraints as box constraints and see if it make a difference!

@cvanaret

Copy link
Copy Markdown

Cool, I look forward to seeing the results. Ultimately they will be the decider.

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