[Design] Should a constraint bound be allowed to depend on the model? Levels of relaxation

Aperta
#401 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@jbcaillau ci sta già lavorando.

Dal 1/11/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

dev

Written by Claude (Claude Code), at the request of ocots, from an exploration of the question. The analysis and the prototype are mine; the position of ocots is given at the end. The question to decide is for the maintainers.

@jbcaillau: your opinion is asked, see the question at the end.

Context

A constraint is written lb ≤ expression ≤ ub, and the bounds must not depend on the state, the control, the variable or the time. Since #343 a bound that does is rejected with a ParsingError; before, it leaked an internal name. PR #396 (for #352) makes the error say how to rewrite the constraint. This issue asks whether the parser should keep refusing, or do the rewriting itself.

Why the rule exists

It is not only a simplification of the parser. The generated code always calls constraint!(…; f=…, lb=…, ub=…), and the model stores each constraint as lb ≤ f(x, u, v, t) ≤ ub with constant lb and ub. This is the form of an optimal control problem and of the NLP it is discretised into, and a box constraint (x₂(t) ≥ 1) becomes a bound on a variable.

Nothing is undecidable: whether a bound depends on the model is known when the block is parsed (has on the declared symbols), and a ≤ b is always equivalent to a - b ≤ 0. A constraint with a bound that depends on the model is a different constraint written in a different place.

What I tried

A throwaway prototype (not pushed) that rewrites instead of refusing, in p_constraint!, before the type of the constraint is chosen:

  • 1 ≤ x₂(t) → swap the sides: x₂(t) ≥ 1. Same constraint, still a box constraint.
  • x₁(t) ≤ u(t) → x₁(t) - u(t) ≤ 0 (lb = -Inf, ub = 0), a path constraint.
  • x₂(2) == v → x₂(2) - v == 0, a boundary constraint.

With the :fun backend the model is the one of the hand-written form (numbers checked: f = 2 for x₁ ≤ u at x = [3, 0], u = 1; f = 5 for x₂(2) == v at xf = [5, 7], v = 2). With the :exa backend the NLP is identical to the hand-written form (number of variables and constraints, lvar, uvar, lcon, ucon, and cons at a random point) for the scalar cases.

What has to be settled

  1. Sign of the multipliers. a ≤ b rewritten as a - b ≤ 0 or as b - a ≥ 0 changes the orientation of the dual. A rule must be fixed and documented (left minus right, relation kept), which matters for the duals by label (#382).
  2. Vector expressions. A scalar bound on a vector expression is not broadcast today, see #400 (DimensionMismatch with :fun, silently 0 with :exa). A rewriting of a vector constraint would hit it, so it has to be fixed first, or the rewriting has to build bounds of the right dimension.
  3. Two-sided constraints with a bound that depends on the model (0 ≤ x₂(0) ≤ v). They would have to be split in two, and the label duplicated. I would keep them refused.
  4. Tests on both backends, and the type of the constraint (box, path, boundary) recomputed after the rewriting.

Levels

Relaxing is not breaking (what is refused today would be accepted), whereas tightening later would be. So it can be done in steps:

Level Behaviour Risk
0 (PR #396, current) refuse, with a rewriting suggested from the constraint itself none
1 swap the sides when only the bound depends on the model and the middle does not (1 ≤ x₂(t) → x₂(t) ≥ 1) none: same constraint, same type, same dual
2 also move the term to the left when both sides depend on the model (x₁(t) ≤ u(t) → x₁(t) - u(t) ≤ 0), and equalities (x₂(2) == v) duals (point 1), vector expressions (point 2)
3 also two-sided constraints, by splitting labels, duals

Position of ocots

Level 0 is a good first step and PR #396 is it. ocots would go as far as level 1 and stop there: the freedom it gives is free of ambiguity (1 ≤ x₂(t) can only mean x₂(t) ≥ 1), while from level 2 the parser would choose a form, and with it the sign of the duals, in place of the user. The point of the exploration was to see what is possible without a reason for the rigid syntax; there is none that forbids it, only the choices of points 1 to 4.

Question

For @jbcaillau: do you see a reason for the current rigidity that I do not (for instance in how the constraints are used downstream, in CTDirect or in the plots), and where would you stop: level 0, 1 or 2?

Related: #343, #352, #400.

Lingua principale
Julia
Stelle
3
Fork
0
Merge medio
1g 32m
PR unite (30g)
18

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di control-toolbox/CTParser.jl

Tutte le issue di control-toolbox/CTParser.jl

Issue simili

Altre issue su Julia

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.