From 0825578778c833ad0414dd8a866213b2d562ee86 Mon Sep 17 00:00:00 2001 From: forbes-0023 Date: Sun, 22 Feb 2026 21:06:21 -0600 Subject: [PATCH] fix(solver): build weight vector after pre-passes to match free param count The weight vector was built before substitution_pass and single_equation_pass, which can fix variables and reduce the free parameter count. This caused a shape mismatch in newton_solve when the Jacobian had fewer columns than the weight vector had entries: ValueError: operands could not be broadcast together with shapes (55,27) (1,28) Move build_weight_vector() after both pre-passes so its length matches the actual free parameters used by the Jacobian. --- kindred_solver/solver.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kindred_solver/solver.py b/kindred_solver/solver.py index f6467b5..c91a679 100644 --- a/kindred_solver/solver.py +++ b/kindred_solver/solver.py @@ -143,8 +143,6 @@ class KindredSolver(kcsolve.IKCSolver): system.constraint_indices, system.params, ) - weight_vec = build_weight_vector(system.params) - if half_spaces: post_step_fn = lambda p: apply_half_space_correction(p, half_spaces) else: @@ -154,6 +152,10 @@ class KindredSolver(kcsolve.IKCSolver): residuals = substitution_pass(system.all_residuals, system.params) residuals = single_equation_pass(residuals, system.params) + # Build weight vector *after* pre-passes so its length matches the + # remaining free parameters (single_equation_pass may fix some). + weight_vec = build_weight_vector(system.params) + # Solve (decomposed for large assemblies, monolithic for small) jac_exprs = None # may be populated by _monolithic_solve if n_free_bodies >= _DECOMPOSE_THRESHOLD: @@ -255,7 +257,6 @@ class KindredSolver(kcsolve.IKCSolver): system.constraint_indices, system.params, ) - weight_vec = build_weight_vector(system.params) if half_spaces: post_step_fn = lambda p: apply_half_space_correction(p, half_spaces) @@ -265,6 +266,10 @@ class KindredSolver(kcsolve.IKCSolver): residuals = substitution_pass(system.all_residuals, system.params) residuals = single_equation_pass(residuals, system.params) + # Build weight vector *after* pre-passes so its length matches the + # remaining free parameters (single_equation_pass may fix some). + weight_vec = build_weight_vector(system.params) + # Build symbolic Jacobian + compile once from .codegen import try_compile_system