Numerical Methods
Jacobian computation, residual formulation, sparse linear algebra, and convergence criteria.
Numerical Methods
This page covers the numerical foundations: how residuals and Jacobians are computed, how the sparse linear algebra works, and what convergence criteria the solver uses.
Analytical Jacobian vs Numerical
The solver computes analytical Jacobians for all 47+ constraint types. Each constraint type has a hand-derived partial derivative implementation in jacobian.rs:
// PointOnCircle: residual = (px-cx)² + (py-cy)² − r² // ∂r/∂px = 2(px−cx), ∂r/∂cx = −2(px−cx), ∂r/∂r = −2r
A numerical fallback using central differences is available for debugging:
∂r_i/∂x_j ≈ (r_i(x_j + h) − r_i(x_j − h)) / (2h), h = 1e-7
The proptest suite (50 random cases per constraint type) verifies that analytical and numerical Jacobians match within tolerance: absolute < 1e-4, relative < 1e-2.
Residual Formulation
Each constraint contributes one or more scalar residuals to the vector r(x). Examples:
| Constraint | Residual |
|---|---|
Distance(a, b, d) | √((ax−bx)² + (ay−by)²) − d |
Horizontal(a, b) | ay − by |
Vertical(a, b) | ax − bx |
PointOnCircle(p, c) | (px−cx)² + (py−cy)² − r² |
TangentLineCircle(l, c) | Distance from circle center to line minus radius |
Angle(l1, l2, θ) | atan2(…) − θ (with branch handling) |
Angular constraints require special treatment due to branch ambiguity (e.g., θ vs θ + π). The solver detects the nearest branch and uses that for the residual.
Normal Equations (LM)
For the Levenberg-Marquardt algorithm, the normal equations are:
(JᵀJ + λ · diag(JᵀJ)) · Δx = −Jᵀr
The damping term λ · diag(JᵀJ) serves two purposes:
- Positive definiteness — ensures the system is solvable even when
JᵀJis singular - Step control — large λ → small steps (gradient descent), small λ → large steps (Gauss-Newton)
The initial λ is set to 1e-3 · max(diag(JᵀJ)) and adapted based on step acceptance.
Sparse Jacobian (CSC Format)
For large problems, the Jacobian is stored in Compressed Sparse Column (CSC) format:
J = [col_ptr, row_indices, values]
Each constraint touches at most 6–8 parameters, giving a very sparse Jacobian. For a 200-parameter problem with 100 constraints, the Jacobian has at most 800 nonzeros out of 20,000 entries (4% density).
SpGEMM (sparse matrix–matrix multiplication) computes A = JᵀJ without forming dense intermediates. The result is another sparse matrix used by the Conjugate Gradient solver.
Convergence Criteria
The solver uses three convergence checks:
- Residual tolerance:
‖r‖₂ < tol(default: 1e-6) - Step tolerance:
‖Δx‖₂ < xtol(default: 1e-10) - Gradient tolerance:
‖Jᵀr‖∞ < gtol(default: 1e-8)
Any one of these is sufficient to declare convergence. The solver also enforces a maximum iteration count (default: 100) to prevent infinite loops.
Radius Clamping and Radmin
Circle and arc radii are clamped to radmin = 1e-12 to prevent degenerate geometry:
- During solving, if
r < radmin, the radius is clamped and a warning is emitted - This prevents division by zero in tangent and point-on-circle residuals
- The clamping is reversible: the unclamped value is restored on solve failure
Warm Start and Drag Mode
In interactive CAD applications, the solver is called repeatedly as the user drags geometry. Warm start uses the previous solution as the initial guess:
- Typically converges in 2–5 iterations (vs 20–50 from a cold start)
- The parameter vector is preserved between calls
- Only changed constraints trigger recomputation of the Jacobian structure
Drag mode pins the dragged entity's position as a constraint, ensuring the solver respects the user's input.
Underconstrained Handling
When DOF > 0, the system is underconstrained. Two strategies are available:
- SoftAnchor — adds weak springs
k·(x − x₀)that resist deviation from the initial guess - MinimumNorm — uses SVD to find the minimum-norm solution among all solutions satisfying the constraints
MinimumNorm is more expensive (O(n³) for the SVD) but produces smoother motion in interactive scenarios.
Parameter Reduction
The normalize.rs module eliminates constraints that can be expressed as parameter substitutions:
| Constraint | Reduction |
|---|---|
Coincident | Merge two points → same parameter slice |
Horizontal | Set y₁ = y₂ |
Vertical | Set x₁ = x₂ |
EqualRadius | Set r₁ = r₂ |
Concentric | Set (cx₁, cy₁) = (cx₂, cy₂) |
EqualMinorAxes | Set b₁ = b₂ |
After reduction, the parameter vector is compacted and a mapping is maintained to expand the solution back to the original parameters.
Constraint Propagation
Twenty deduction rules in solver/propagation.rs infer additional constraints from existing ones:
Horizontal ∧ Vertical → PerpendicularCoincident ∧ Horizontal → SameYEqualRadius ∧ Concentric → SameCircle
These deductions are applied before solving to tighten the constraint graph and improve convergence.