You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
uv-first docs, PyPI/GitHub README parity, and automatic carry-at-9 versioning (#26)
- README: lead with uv (uv add / uv sync), keep pip as a documented fallback;
convert image/file links to absolute URLs so the PyPI long-description (which
is README.md) renders identically to GitHub
- docs/README.md: sync to the canonical README (was stale: old rigged LQR
comparison and figures)
- tools/bump_version.py: increment the version with single-digit carry-at-9
components (2.0.9 -> 2.1.0, 2.9.9 -> 3.0.0), updating pyproject.toml and
__init__.py together
- publish workflow: auto-increment the version (commit to master) before
building/publishing/releasing, so every release bumps automatically
- CONTRIBUTING: document the auto-bump release flow
- CLAUDE.md: record the uv-first + carry-at-9 versioning conventions
Claude-Session: https://claude.ai/code/session_013KvuS9HKbnZAwwFJyBkyHc
Co-authored-by: Claude <noreply@anthropic.com>
@@ -92,7 +102,10 @@ uv sync --extra dev # creates .venv with the exact locked dependencies
92
102
uv run pre-commit install # enable the formatting / lint / type-check hooks
93
103
```
94
104
95
-
Then run anything through `uv run` (`uv run pytest`, `uv run python -m PyDiffGame.examples.MassesWithSpringsComparison`, …).
105
+
Then run anything through `uv run` (`uv run pytest`,
106
+
`uv run python -m PyDiffGame.examples.MassesWithSpringsComparison`, …). Pip users
107
+
can instead `pip install -e ".[dev]"`, though uv is recommended for the exact
108
+
locked environment.
96
109
97
110
# Quick start
98
111
@@ -183,7 +196,7 @@ To show the package in action we compare a differential game against an LQR on a
183
196
masses connected by springs — a textbook coupled, oscillatory system:
184
197
185
198
<palign="center">
186
-
<img alt="Two masses connected by springs between two walls" src="images/readme/masses_schematic.png" width="760"/>
199
+
<img alt="Two masses connected by springs between two walls" src="https://raw.githubusercontent.com/krichelj/PyDiffGame/master/images/readme/masses_schematic.png" width="760"/>
187
200
</p>
188
201
189
202
The physical input space is decomposed along the **modal** directions of $M^{-1}K$, so each
@@ -245,19 +258,19 @@ monolithic optimum **to numerical precision**: the two state trajectories coinci
245
258
(they differ by ~10⁻⁷) and the costs are equal:
246
259
247
260
<palign="center">
248
-
<img alt="State trajectories: the decomposed game reproduces the monolithic LQR" src="images/readme/masses_game_vs_lqr.png" width="860"/>
261
+
<img alt="State trajectories: the decomposed game reproduces the monolithic LQR" src="https://raw.githubusercontent.com/krichelj/PyDiffGame/master/images/readme/masses_game_vs_lqr.png" width="860"/>
249
262
</p>
250
263
251
264
<palign="center">
252
-
<img alt="Cost comparison: the modal game recovers the LQR optimum" src="images/readme/masses_cost.png" width="440"/>
265
+
<img alt="Cost comparison: the modal game recovers the LQR optimum" src="https://raw.githubusercontent.com/krichelj/PyDiffGame/master/images/readme/masses_cost.png" width="440"/>
253
266
</p>
254
267
255
268
For this modally-decoupled system the decomposition is **lossless** — and it buys
256
269
**compositionality**: you can add, drop or re-weight a control task by editing a single
257
270
player, without re-tuning one monolithic cost matrix.
258
271
259
272
> The figures above are regenerated from the live solver by
0 commit comments