# Story 4.8: Jacobian-Freezing Optimization Status: done ## Story As a performance-critical user, I want to freeze Jacobian updates when approaching the solution, so that CPU time is reduced significantly without losing convergence stability. ## Acceptance Criteria 1. **Enable Jacobian Freezing in Configuration** - Given a `NewtonConfig` - When configured with `with_jacobian_freezing(max_frozen_iters)` (and optionally threshold) - Then the solver uses the frozen Jacobian optimization when conditions permit 2. **Reuse Jacobian Matrix** - Given a system solving with Newton-Raphson - When the freezing condition is met - Then the solver skips calling `traverse_for_jacobian` and reuses the previous `JacobianMatrix` - And speed per iteration improves significantly (up to ~80% target) 3. **Auto-disable on Residual Increase** - Given the solver is using a frozen Jacobian - When the current iteration's residual is GREATER than the previous iteration's residual - Then freezing is automatically disabled, and the Jacobian is recomputed for the next step 4. **Backward Compatibility** - Given an existing `NewtonConfig` - When no freezing configuration is provided - Then the solver behaves exactly as before (recomputing Jacobian every step) ## Tasks / Subtasks - [x] Add `JacobianFreezingConfig` to `NewtonConfig` - [x] Add `jacobian_freezing: Option` field - [x] Add builder method `with_jacobian_freezing()` - [x] Implement Modified Newton logic in `crates/solver/src/solver.rs` - [x] Track frozen iterations counter - [x] Skip Jacobian update when conditions permit (residual decreasing, counter < max) - [x] Force Jacobian update if residual increases - [x] Fix broken test struct literals - [x] Update `crates/solver/tests/*.rs` with `..Default::default()` for `NewtonConfig` where new fields are added (if not using builders) - [x] Add unit/integration tests - [x] Test frozen Jacobian converges correctly - [x] Test auto-recompute on divergence trend ## Dev Notes - Relevant architecture patterns and constraints: - **Zero Allocation**: Do not dynamically allocate new matrices in the hot path. The `JacobianMatrix` should be reused. - **Safety**: No panics allowed. - Source tree components to touch: - `crates/solver/src/solver.rs` (update Newton solver loop) - `crates/solver/tests/newton_raphson.rs` (add integration tests for freezing) - Testing standards summary: - Use `approx::assert_relative_eq!` for floating point assertions. - Run `cargo clippy -- -D warnings` to ensure style compliance. ### Project Structure Notes - Alignment with unified project structure: all changes are safely confined to the `solver` crate. No bindings code required. ### References - [Source: `epics.md` FR19] "Solver can freeze Jacobian calculation to accelerate" - [Source: `epics.md` Story 4.8] "Jacobian-Freezing Optimization" - [Source: `architecture.md` NFR4] "No dynamic allocation in solver loop (pre-calculated allocation only)" ## Dev Agent Record ### Agent Model Used Antigravity ### Debug Log References ### Completion Notes List - Ultimate context engine analysis completed - comprehensive developer guide created - Implemented `JacobianFreezingConfig` struct with `max_frozen_iters` and `threshold` fields - Added `jacobian_freezing: Option` to `NewtonConfig` with `None` default for backward compatibility - Added `with_jacobian_freezing()` builder method on `NewtonConfig` - Modified Newton-Raphson solve loop: decision logic skips Jacobian assembly when frozen, stores and reuses `JacobianMatrix` via `.clone()` - Implemented auto-disable: sets `force_recompute = true` when residual ratio exceeds `(1.0 - threshold)`, resets frozen counter - Fixed inline struct literal in existing test (`test_with_timeout_preserves_other_fields`) - Exported `JacobianFreezingConfig` from `lib.rs` - Created 12 new integration tests in `jacobian_freezing.rs` covering all ACs - All 151 tests pass (130 unit + 21 integration) + 17 doc-tests; zero regressions - **[AI Code Review Fixes]**: Fixed critical Zero-Allocation architecture violation by pre-allocating `JacobianMatrix` outside the loop and adding in-place `update_from_builder` logic to avoid `clone()`. - **[AI Code Review Fixes]**: Added missing Rustdoc documentation to `JacobianFreezingConfig` public fields. - **[AI Code Review Fixes]**: Fixed integration test file `jacobian_freezing.rs` not being tracked in git natively. ### File List - `crates/solver/src/solver.rs` (modified — added struct, field, builder, loop logic) - `crates/solver/src/lib.rs` (modified — exported `JacobianFreezingConfig`) - `crates/solver/tests/jacobian_freezing.rs` (new — 12 integration tests) - `_bmad-output/implementation-artifacts/4-8-jacobian-freezing-optimization.md` (modified — status + checkboxes + dev notes) ## Change Log - 2026-02-19: Implemented Jacobian-Freezing Optimization (Story 4.8) — all ACs satisfied, 12 tests added, zero regressions