Skip to content

perf(engine): an update that gives a linear constraint's coefficients new numbers at the coordinates they had skips most of the build - #2071

Open
FBumann wants to merge 4 commits into
perf/update-numbers-onlyfrom
perf/update-coefficients
Open

FBumann wants to merge 4 commits into
perf/update-numbers-onlyfrom
perf/update-coefficients

Conversation

@FBumann

@FBumann FBumann commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Prompt: Do the coefficient one first

Note

The following content was generated by AI.

An update that changes a linear constraint's coefficients, at the coordinates they had, now keeps the labels and re-reads only the matrix entries of the constraints it feeds. On top of #2070, such an update takes 0.25–0.49× the time. The solver still loads again.

What changed

#2070 counted a parameter that scales a variable as structural, so any such update built in full. Over the ports, those were 67 of the 87 single-parameter updates that missed #2070's path.

The held solver is untouched. A changed coefficient fails its keeps() test, so the next solve loads the model again. Taking changed coefficients in place (HiGHS changeCoeff, Gurobi chgCoeffs) is the next PR.

Docs: docs/howto/faster.md and the labels paragraph of docs/development/architecture.md name coefficients and say the solver still loads again.

Probes and reach

tests/test_update.py:

Outside the suite: storage xs eta and network s eta take the path, and their hand-off and diagnostics equal a fresh build's.

Mutation table

Each guard broken by one text substitution, the full suite run (pytest -q -n 4), the file restored with git checkout --, __pycache__ dropped on both sides, the tree clean after each. The first six at 6db4b14, the last three at 8b6ac48.

mutation result
coefficients never read again caught, 37 failed
a quadratic constraint's coefficients not structural caught, 1 failed
a factor of a product not a coefficient caught, 36 failed
a divisor not a coefficient caught, 4 failed
rows placed densely under a where caught, 5 failed
the rows after a splice not moved caught, 9 failed
the coefficient range not read again still green at 6db4b14; caught, 26 failed, by the diagnostics comparison added for it
the nonzero count not read again still green at 6db4b14; caught, 11 failed, the same way
coefficient ranges left where a refill put them caught, 16 failed

The diagnostics comparison also found that #2070 listed a re-read bound or right-hand-side range at the end of its frame. That is fixed on #2070 (c70193d) and merged here.

A/B of the update, this container (4 cores, not the publishing machine)

Base #2070's head c70193d, head 8b6ac48. One fresh process per measurement, base and head alternated ABBA for three rounds. Each process builds once, then times 12 calls of model.update({param: …}), each with the values ×0.9–1.1 at the same coordinates, and reports the median; the table gives the median over the three processes. Only the update is timed: no solve, so the solver's reload is not in these numbers. Started once the load average fell below 1.5: 1.36 before, 1.31 after.

model, what changes base ms head ms head ÷ base head faster in
storage xs (10k columns), eta 36.4 17.9 0.49 3/3
storage s (100k columns), eta 52.2 25.6 0.49 3/3
network s (100k columns), eta 104.8 37.6 0.36 3/3
network m (1M columns), eta 352.4 86.9 0.25 3/3
storage xs, load, control: on #2070's path either way 11.2 11.1 0.99 2/3

Per-process medians, ms: storage xs eta base 39.5 / 35.3 / 36.4, head 18.5 / 17.9 / 16.2; storage s base 47.2 / 55.1 / 52.2, head 21.6 / 25.9 / 25.6; network s base 104.8 / 113.9 / 98.2, head 37.6 / 35.1 / 38.4; network m base 372.0 / 352.4 / 336.3, head 85.0 / 93.4 / 86.9; control base 9.8 / 11.3 / 11.2, head 11.1 / 11.1 / 11.0.

Gates at 8b6ac48, on mathspec 0.4.0

No pixi in this container, so each gate ran in a uv environment synced from #2065's uv.lock (--group dev --group docs --all-extras, Python 3.12, polars 2.0.0):

  • ruff check ., ruff format --check . (0.16.9): clean.
  • pyrefly check on the package (1.3.1): 0 errors.
  • pytest -q -n 4: 5945 passed, 459 skipped, 4 xfailed.
  • pytest tests/test_docs_math.py tests/test_docstring_links.py (docs-test): 116 passed at 6db4b14.

Not run: docs-build, the floors environment, test-via-pyarrow.

Not done. The solver still loads again on a changed coefficient, which is the next PR. Two smaller cases still build in full: a where whose answer does not change (11 port updates) and a constraint that dropped a row (9). Stacked on #2070.

🤖 Generated with Claude Code

https://claude.ai/code/session_018B3nA4oeSor7jjZ4vwux1G

… new numbers at the coordinates they had skips most of the build

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B3nA4oeSor7jjZ4vwux1G
…erf/update-coefficients

# Conflicts:
#	src/specsolve/_engine/assembly.py
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018B3nA4oeSor7jjZ4vwux1G
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f16912e1-dbb0-42fb-83be-4d76eadf81a3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@read-the-docs-community

read-the-docs-community Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

@codspeed

codspeed Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

1 warning

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 5 improved benchmarks
❌ 1 regressed benchmark
✅ 98 untouched benchmarks
⏩ 307 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_read[commitment-s-specsolve-parquet] 220.7 KB 684.3 KB -67.75%
⚡ test_window[sector-s-specsolve-highs-coefficient] 7.8 MB 5.4 MB +43.59%
⚡ test_window[network-s-specsolve-highs-coefficient] 19.7 MB 15.2 MB +30.08%
⚡ test_window[commitment-s-specsolve-highs-coefficient] 2 MB 1.6 MB +27.56%
⚡ test_window[storage-s-specsolve-highs-coefficient] 16.1 MB 12.8 MB +25.69%
⚡ test_window[commitment-s-specsolve-highs-one] 1,157.1 KB 938.2 KB +23.33%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/update-coefficients (6a9c249) with perf/update-numbers-only (1de322f)

Open in CodSpeed

Footnotes

  1. 307 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. ↩

This branch has not been deployed

No deployments
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