Skip to content

Read epsilon-constraint duals as frontier slope / exchange rate in multi-objective skill#1406

Draft
cafzal wants to merge 6 commits into
NVIDIA:mainfrom
cafzal:claude/elastic-goodall-3693ff
Draft

Read epsilon-constraint duals as frontier slope / exchange rate in multi-objective skill#1406
cafzal wants to merge 6 commits into
NVIDIA:mainfrom
cafzal:claude/elastic-goodall-3693ff

Conversation

@cafzal

@cafzal cafzal commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Description

Extends the per-type dual/sensitivity note from #1393 into the cuopt-multi-objective-exploration skill. #1393 established what duals are per problem type (shadow price, reduced cost; none for MILP); this adds the frontier-specific fact that follows: the dual on a swept ε-constraint is the local slope of the Pareto frontier — the exchange rate between the two objectives at that point, available off each LP/QP solve, and a signal for where to spend solves on the sweep.

Why. Step 5 of the skill already asks the agent to report the tradeoff exchange rate; on LP/QP that number is the swept constraint's dual (the local slope), where differencing two solved points only gives a secant across any kink between them. The same dual also shows where the curve bends, so the sweep can refine there instead of over a uniform grid. This is the downstream decision-layer consumer #1393 was written for.

Scope. LP/QP only — MILP optima have no duals (stated in every new note; cross-refs defer to the formulation skill rather than restating it). The dual must be read off a linear ε-constraint: cuOpt returns no dual for a quadratic constraint, so a quadratic objective stays the objective and the linear objectives are ε-constrained. Kept to interpretation plus a sweep-budget principle; no predict-and-extrapolate procedure, since a one-sided dual at a degenerate vertex can mislead past a breakpoint, and at a knee the slope is two-sided.

Dual accuracy (deliberate wording). The skill says the exchange rate is the dual "accurate to the solve's optimality tolerance," not "exact." cuOpt's default LP method is Concurrent — it races PDLP (first-order), dual simplex, and barrier — so a returned dual is exact-basic only when dual simplex solved it and tolerance-bounded otherwise (solved_by reports which). Adaptive refinement therefore treats slope changes below the tolerance as solver noise. This matches the "PDLP-tolerance caveat" already noted in #1355.

Tests. Adds a QP risk-return eval exercising the dual-as-exchange-rate reading, and clarifies the existing MILP eval to estimate the rate by differencing. QP duals are returned by cuOpt's barrier solver (extended for SOCP and general convex quadratic constraints in 26.06, #1290 / #1361); the eval sweeps a linear return floor, whose dual is returned — cuOpt returns no dual for a quadratic constraint.

Validation & gating. ci/utils/validate_skills.sh passes; evals.json is valid (4 tasks); the skill has no executable assets. BENCHMARK.md, the skill card, and skill.oms.sig are regenerated by the NVSkills-Eval pipeline, which is the gate and needs a maintainer /nvskills-ci (fork branches can't self-validate).

Related. Builds on #1393. Exposing the basis/variable-status and RHS/objective ranging that would pin the frontier's breakpoints exactly is tracked in #1394 / #1395.

Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

Companion PRs — coordinated follow-ons to #1393, catching the numerical-optimization skills up to cuOpt's 26.06 QP / quadratic-constraint additions:

Shared verified boundary across all three: a QP with linear constraints returns duals (the barrier solver is primal-dual); any quadratic constraint suppresses duals for the whole solve (cpp/src/pdlp/solve.cu NaN-fills the dual/reduced-cost arrays). MILP has no duals.

…lti-objective skill

Extends the dual/sensitivity work from NVIDIA#1393 into the multi-objective layer:
the dual on a swept epsilon-constraint is the local slope of the Pareto
frontier (the exchange rate between the two objectives), available off each
LP/QP solve at no extra cost.

- Step 3: note that the swept constraint's dual is the frontier's local
  tangent; defers "what duals are / which problem types expose them" to the
  formulation skill (no API symbols inlined).
- Step 4: a practical-notes bullet to refine the sweep where the slope
  changes, spending fewer solves than a uniform grid (LP/QP; MILP falls back
  to gaps between primal objective values).
- Step 5: the exchange rate is read from the dual on LP/QP (differencing on
  MILP), and the epsilon-constraint duals are the implicit weights per point.
- evals: add a QP risk-return frontier task exercising dual-as-exchange-rate;
  clarify the MILP supplier eval to estimate the rate by differencing.

Concepts/workflow skill: LP/QP-only scoping preserved, no maturity labels.
BENCHMARK.md, the skill card, and the signature are regenerated by NVSkills CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

cafzal and others added 5 commits June 8, 2026 15:29
Addresses duality-review feedback (a PDLP co-author's lens). cuOpt's default
LP method is Concurrent — it races PDLP (first-order), dual simplex, and
barrier — so a returned dual is exact-basic only when dual simplex solved it
and accurate only to the optimality tolerance otherwise. Three concepts-level,
stale-robust edits (no method names in the skill text):

- Step 5: drop "exact and local"; the exchange rate is the dual "accurate to
  the solve's optimality tolerance (tighten it before relying on a dual)."
- Step 4: treat a slope change as a knee only when it exceeds the solve
  tolerance — smaller differences are solver noise, not curvature.
- Step 5: at a knee the slope is two-sided; quote the exchange rate as a range.

Consistent with the "PDLP-tolerance caveat" already noted in NVIDIA#1355.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
Verified against the cuOpt repo + docs. Two corrections:

- cuOpt returns no dual variables for problems with quadratic constraints
  (docs/cuopt/source/cuopt-c/convex/convex-examples.rst). The skill offers a
  quadratic ε-constraint (xᵀQx ≤ ε) option, which would have no dual to read.
  Step 3 now says: read the dual off a *linear* ε-constraint — keep a
  quadratic objective as f1 and ε-constrain the linear objectives.
- Switch the new eval from a QP risk-return frontier to an LP cost-vs-quality
  blend. LP duals are the tested, documented path (lp_duals asset); QP-dual
  reading is untested and the api-* skills scope duals to "LP only", so the
  eval should exercise the verified path. Skill prose stays "LP/QP" per the
  formulation skill.

Separate (not in this PR): the formulation skill's "no quadratic constraints"
is stale (cuOpt supports convex quadratic constraints via barrier/SOC), and the
api-python/api-c "dual values (LP only)" likely needs "LP/QP, linear
constraints" — flagged for follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
Reverts the over-conservative QP->LP eval switch. Git history confirms QP
duals are real and recent: barrier extended for SOCP (NVIDIA#1290, 2026-05-30) and
general convex quadratic constraints (NVIDIA#1361, 2026-06-02); the barrier solver
is primal-dual and pdlp/solve.cu returns dual_solution + reduced_cost for the
Barrier method. The api-python/api-c "dual values (LP only)" wording predates
this (NVIDIA#1183, 2026-05-07) and is the stale part, not the formulation skill's
"LP/QP" (NVIDIA#1393).

The QP risk-return eval sweeps a *linear* return floor, whose dual is returned
(cuOpt returns no dual for a quadratic constraint, so the quadratic stays the
objective). Skill prose unchanged (already "LP/QP" + linear-constraint caveat).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
A zero shadow price means the constraint is slack (not binding), so during a
sweep a zero dual on an ε-bound signals the objectives aren't trading off
there — the sweep has run past the frontier's edge. Small precision/diagnostic
addition; consistent with the dual-as-slope content already in Step 3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
…vior

cpp/src/pdlp/solve.cu (has_quadratic_constraints -> thrust::fill the entire
dual_solution + reduced_cost with quiet_NaN) shows a single quadratic
constraint suppresses duals for the *whole* solve, not just that constraint's
row. Reword "no dual for a quadratic constraint" -> "any quadratic constraint
makes cuOpt return no duals for the whole solve" in Step 3 and the QP eval, for
consistency with PRs NVIDIA#1407 (formulation) and NVIDIA#1408 (api skills, "NaN-filled").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: cafzal <cameron.afzal@gmail.com>
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.

1 participant