fix(solver): skip single_equation_pass during drag to prevent stale constraints #329

Merged
forbes merged 1 commits from fix/planar-drag-prepass into main 2026-02-25 19:03:41 +00:00
Owner

Problem

Planar distance=0 constraints weren't holding during interactive drag operations.

Root Cause

single_equation_pass in pre_drag() analytically solves variables from upstream constraints (e.g. Coincident fixes bracket/tz = 0) and bakes their values as Const() nodes into downstream residual expressions. It also calls params.fix() on those variables, removing them from the free list.

During drag, the cached residuals use these stale constants even though part positions have changed via set_value(). The fixed variables aren't in Newton's free list, so they can't be corrected. Downstream constraints (like Planar) that had upstream values baked in silently stop being enforced.

Fix

Skip single_equation_pass in the pre_drag() path. Only substitution_pass (which replaces genuinely grounded/immutable parameters) is safe to cache across drag steps. Newton-Raphson converges in 1-2 iterations from a nearby initial guess anyway, so the prepass optimization is unnecessary for drag performance.

The static solve() path continues to use single_equation_pass as before.

Testing

  • 5 new regression tests in tests/test_drag.py covering the bug scenario and fix
  • Full test suite passes (291 tests, 0 failures)

Changes

  • mods/solver/kindred_solver/solver.py — remove single_equation_pass from pre_drag()
  • mods/solver/tests/test_drag.py — new regression test file
## Problem Planar distance=0 constraints weren't holding during interactive drag operations. ## Root Cause `single_equation_pass` in `pre_drag()` analytically solves variables from upstream constraints (e.g. Coincident fixes `bracket/tz = 0`) and bakes their values as `Const()` nodes into downstream residual expressions. It also calls `params.fix()` on those variables, removing them from the free list. During drag, the cached residuals use these stale constants even though part positions have changed via `set_value()`. The fixed variables aren't in Newton's free list, so they can't be corrected. Downstream constraints (like Planar) that had upstream values baked in silently stop being enforced. ## Fix Skip `single_equation_pass` in the `pre_drag()` path. Only `substitution_pass` (which replaces genuinely grounded/immutable parameters) is safe to cache across drag steps. Newton-Raphson converges in 1-2 iterations from a nearby initial guess anyway, so the prepass optimization is unnecessary for drag performance. The static `solve()` path continues to use `single_equation_pass` as before. ## Testing - 5 new regression tests in `tests/test_drag.py` covering the bug scenario and fix - Full test suite passes (291 tests, 0 failures) ## Changes - `mods/solver/kindred_solver/solver.py` — remove `single_equation_pass` from `pre_drag()` - `mods/solver/tests/test_drag.py` — new regression test file
forbes added 1 commit 2026-02-25 18:58:24 +00:00
fix(solver): update solver submodule — skip prepass during drag
Some checks failed
Build and Test / build (pull_request) Has been cancelled
314955c3ef
Updates solver submodule to include fix for planar distance=0
constraints not holding during drag operations.
forbes merged commit 82f2422285 into main 2026-02-25 19:03:41 +00:00
forbes deleted branch fix/planar-drag-prepass 2026-02-25 19:03:50 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/create#329