Skip to content

docs(api): a dual is the rate at which the optimal objective rises with its row's right side, on every sink - #1774

Merged
FBumann merged 5 commits into
mainfrom
claude/mathspec-analysis-features-zy7x4n-dual-sign
Sep 29, 2026
Merged

FBumann merged 5 commits into
mainfrom
claude/mathspec-analysis-features-zy7x4n-dual-sign

Conversation

@FBumann

@FBumann FBumann commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator

Prompt: Let's do a pr in mathspec and specsolve fixing the dual ambiguity

Note

The following content was generated by AI.

Result.dual states its sign: the rate at which the optimal objective rises with the row's right side, for every comparator and sense. That is the definition energy-models/mathspec#751 gives dual(c). A test pins it on every sink against finite differences.

Method, mutation, gates, departures

What was ambiguous

Result.dual said "shadow prices" and nothing about sign. mathspec's page said a dual is the rate at which the objective improves as the row is relaxed. That makes a <= dual under minimize non-negative, where every sink here returns it non-positive, and it leaves an == row without a sign. No code changes here: all three sinks already return the rate in the right side, as written in the YAML. energy-models/mathspec#751 moves mathspec's definition to match.

The test

test_a_dual_is_the_rate_of_the_optimum_in_its_rows_right_side, in tests/test_duals.py. One row, "p is at least 2 + b", is written six ways: p >= 2 + b, 2 + b <= p, -p <= -2 - b, p - b >= 2, p == 2 + b and 2 + b == p. Each runs under minimize and maximize, on every sink solver_name yields. The dual has to equal the finite difference of the objective in b (step 1e-3; the row binds, so the difference is exact), times how b moves rhs - lhs.

Result: 36 passed, highs, gurobi and xpress. Gurobi and Xpress ran on the size-limited licences their PyPI wheels carry.

Mutation

Mutation Result
HiGHS's row_dual negated in highs.py 12 failed: every HiGHS case

Gates

pixi install could not run in the session: it fetches the conda-to-PyPI mapping from a host the session's network refuses. The gates ran from uv sync --group dev --extra gurobi --extra xpress on Python 3.12, with the tool versions the dev group pins.

  • ruff check .: clean.
  • ruff format --check .: clean.
  • pyrefly check, both configs: 0 errors.
  • pytest -q -n auto: 4078 passed, 320 skipped, 1 xfailed.

docs-build was not run.

Not done

  • examples/benders/run.py already reads the slope with this sign, so it is unchanged.
  • dual_ray has its own convention, stated in test_dual_ray.py, and is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS


Generated by Claude Code

…th its row's right side, on every sink

Result.dual now states the sign: of lhs <= rhs, the rate in d of
lhs <= rhs + d, for every comparator and under either sense. A test pins it
against a finite difference for six ways of writing one row, under minimize
and maximize, on every installed sink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvcU9JRnsinc9e9syvVcHS
@read-the-docs-community

read-the-docs-community Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Documentation build overview

📚 specsolve | 🛠️ Build #34825053 | 📁 Comparing 9b1278d against latest (e78c1a3)

  🔍 Preview build  

4 files changed
± about/architecture/index.html
± about/changelog/index.html
± reference/api/index.html
± reference/data/index.html

@codspeed

codspeed Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 24 untouched benchmarks
⏩ 82 skipped benchmarks1


Comparing claude/mathspec-analysis-features-zy7x4n-dual-sign (9b1278d) with main (2ff1cff)2

Open in CodSpeed

Footnotes

  1. 82 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (854c6b4) during the generation of this report, so 2ff1cff was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

…sis-features-zy7x4n-dual-sign

# Conflicts:
#	CHANGELOG.md
FBumann pushed a commit that referenced this pull request Sep 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7hhaASVuhgSVvaXkmUpEh
@FBumann FBumann mentioned this pull request Sep 29, 2026
@FBumann
FBumann merged commit 5ce2d56 into main Sep 29, 2026
13 checks passed
@FBumann
FBumann deleted the claude/mathspec-analysis-features-zy7x4n-dual-sign branch September 29, 2026 10:00
FBumann added a commit that referenced this pull request Sep 29, 2026
> **Prompt:** "Merged. Lets prepare a release" … "We want 1774 in there
too!"

> [!NOTE]
> The following content was generated by AI.

Merging this releases 0.2.1. It is a patch: three fixes for wrong
answers that raised no error (#1784, #1779, #1776), the sign of a dual
stated on `Result.dual` (#1774), and no break of a file, archive or
import. The diff is `CHANGELOG.md` alone.

<details><summary>What the section says, and what was checked</summary>

- `## Upcoming version` became `## 0.2.1 (2026-09-29)`, with a paragraph
on who is affected by each fix, a paragraph on the dual's sign, and then
the four pull requests, each listed once. A new, empty `## Upcoming
version` sits above it.
- **Why a patch.** No construct was renamed or refused, no import moved,
and nothing a result, sweep or archive writes to disk changed:
`relational/parquet.py` is unchanged since `v0.2.0`, so `LAYOUT` is too.
`pyproject.toml` changed only tool pins in the `dev` and `docs` groups.
The fixes change values that were wrong, and #1774 changes no value.
- `python -m tools.changelog check` prints `releases 0.2.1 on merge`.
`python -m tools.changelog notes 0.2.1` prints the section as the
release notes. `tests/test_changelog.py` passes.
- Left out, as their types owe no line: #1781 (`test`), #1737 (`test`),
#1761 (`chore`) and #1760 (`ci`).
- On merge, `release.yaml` tags `v0.2.1`, opens the GitHub release,
builds, and waits for a reviewer to approve the `pypi` environment
before it uploads.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Q7hhaASVuhgSVvaXkmUpEh

---------

Co-authored-by: Claude <noreply@anthropic.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.

2 participants