Entropyk/_bmad-output/implementation-artifacts/4-8-jacobian-freezing-optimization.md

104 lines
5.0 KiB
Markdown

# Story 4.8: Jacobian-Freezing Optimization
Status: done
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
## 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<JacobianFreezingConfig>` 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<JacobianFreezingConfig>` 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