Skip to content

Handle dual and reduced cost#221

Open
tbittar wants to merge 25 commits into
developfrom
feature/dual-reduced-cost-clean
Open

Handle dual and reduced cost#221
tbittar wants to merge 25 commits into
developfrom
feature/dual-reduced-cost-clean

Conversation

@tbittar

@tbittar tbittar commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds support for dual variables (constraint shadow prices) and reduced costs (variable marginal costs) as first-class model outputs in GemsPy.

New expression operators

Two new operators are available in model expression strings:

  • dual(constraint_name) — returns the shadow price of a constraint at each time step
  • reduced_cost(variable_name) — returns the reduced cost of a variable at each time step

Both are parsed and validated at model-build time via the visitor pattern (DualNode, ReducedCostNode). ModelIdentifiers now carries the set of constraint names so that dual() arguments can be checked against known constraints.

Simulation table output

SimulationTableBuilder collects and exposes duals and reduced costs alongside other extra outputs:

  • Constraint duals are read from linopy's model.dual dataset; the builder aggregates __eq, __lb, and __ub suffixes into a single value per constraint.
  • Reduced costs require solver-specific extraction because linopy does not expose a unified API for them (see below).

Solver-specific reduced cost support

Linopy provides no generic reduced-cost accessor, so the builder falls back to each solver's native API:

Solver Method Notes
Xpress >= 9.8 solver_model.getLpSol() Returns (x, slack, duals, djs); DJ list used. Checked before getSolution because xpress.problem also exposes getSolution with an incompatible signature.
Gurobi solver_model.getAttr("RC", solver_model.getVars()) Standard Gurobi attribute query.
HiGHS solution.col_dual Column dual from HiGHS solution object.

If the solver model is None or unsupported, reduced costs are silently skipped (returns {}).

Project environment

  • A new solvers optional dependency group (xpress>=9.8, gurobipy>=10.0) is added to pyproject.toml for running solver-specific tests locally with a free licence.
  • CI installs this group (uv sync --group dev --group solvers) so solver-specific tests run in the pipeline.

Pre-commit consistency

  • Removed the redundant --config pyproject.toml argument from the black hook (black discovers pyproject.toml automatically).
  • Simplified the isort hook args to --profile black (removed explicit --filter-files src tests which conflicted with pre-commit's own file filtering).

Test plan

  • pytest tests/unittests/ — unit tests covering new expression nodes, simulation table builder, and fluent accessor API
  • pytest tests/e2e/functional/test_dual_reduced_cost.py — end-to-end tests for studies 10_5, 10_5_1, 10_5_2 (requires solver licence)
  • pre-commit run --all-files — confirm black and isort hooks pass cleanly

Closes #182

🤖 Generated with Claude Code

@tbittar tbittar changed the title WIP : Handle dual and reduced cost Handle dual and reduced cost Jun 9, 2026
@tbittar tbittar force-pushed the feature/dual-reduced-cost-clean branch from 8305925 to 142153d Compare June 9, 2026 11:30
@tbittar tbittar changed the base branch from main to develop June 9, 2026 14:23
@tbittar tbittar force-pushed the feature/dual-reduced-cost-clean branch from 142153d to 79396a5 Compare June 9, 2026 14:34
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.

feat: dual, reduced_cost operators

3 participants