263 lines
7.8 KiB
Markdown
263 lines
7.8 KiB
Markdown
---
|
|
storyId: 6-6
|
|
title: Python Solver Configuration Parity
|
|
epic: 6
|
|
status: done
|
|
created: 2026-02-22
|
|
priority: P0
|
|
---
|
|
|
|
# Story 6.6: Python Solver Configuration Parity
|
|
|
|
## Overview
|
|
|
|
Expose all Rust solver configuration options in Python bindings to enable full control over convergence optimization from Python scripts.
|
|
|
|
## Problem Statement
|
|
|
|
The current Python bindings expose only a subset of the Rust solver configuration options. This prevents Python users from:
|
|
|
|
1. **Setting initial states** for cold-start solving (critical for convergence)
|
|
2. **Configuring Jacobian freezing** for performance optimization
|
|
3. **Using advanced convergence criteria** for multi-circuit systems
|
|
4. **Accessing timeout behavior configuration** (ZOH fallback for HIL)
|
|
5. **Using SolverStrategy** for uniform solver abstraction
|
|
|
|
### Gap Analysis
|
|
|
|
| Rust Field | Python Exposed | Impact | Priority |
|
|
|------------|----------------|--------|----------|
|
|
| `initial_state` | ❌ | Cannot warm-start solver | P0 |
|
|
| `use_numerical_jacobian` | ❌ | Cannot debug Jacobian issues | P1 |
|
|
| `jacobian_freezing` | ❌ | Missing 80% performance optimization | P1 |
|
|
| `convergence_criteria` | ❌ | Cannot configure multi-circuit convergence | P1 |
|
|
| `timeout_config` | ❌ | Cannot configure ZOH fallback | P2 |
|
|
| `previous_state` | ❌ | Cannot use HIL zero-order hold | P2 |
|
|
| `line_search_armijo_c` | ❌ | Cannot tune line search | P2 |
|
|
| `divergence_threshold` | ❌ | Cannot adjust divergence detection | P2 |
|
|
| `SolverStrategy` enum | ❌ | No uniform solver abstraction | P0 |
|
|
|
|
## Acceptance Criteria
|
|
|
|
### AC1: NewtonConfig Full Exposure
|
|
|
|
**Given** Python script using entropyk
|
|
**When** creating `NewtonConfig`
|
|
**Then** all Rust configuration fields are accessible:
|
|
|
|
```python
|
|
config = entropyk.NewtonConfig(
|
|
max_iterations=200,
|
|
tolerance=1e-6,
|
|
line_search=True,
|
|
timeout_ms=5000,
|
|
# NEW FIELDS:
|
|
initial_state=[101325.0, 420000.0, ...], # Warm-start
|
|
use_numerical_jacobian=False,
|
|
jacobian_freezing=entropyk.JacobianFreezingConfig(
|
|
max_frozen_iters=3,
|
|
threshold=0.1
|
|
),
|
|
convergence_criteria=entropyk.ConvergenceCriteria(
|
|
pressure_tolerance_pa=1.0,
|
|
mass_balance_tolerance_kgs=1e-9
|
|
),
|
|
timeout_config=entropyk.TimeoutConfig(
|
|
return_best_state_on_timeout=True,
|
|
zoh_fallback=False
|
|
),
|
|
line_search_armijo_c=1e-4,
|
|
line_search_max_backtracks=20,
|
|
divergence_threshold=1e10
|
|
)
|
|
```
|
|
|
|
### AC2: PicardConfig Full Exposure
|
|
|
|
**Given** Python script using entropyk
|
|
**When** creating `PicardConfig`
|
|
**Then** all Rust configuration fields are accessible:
|
|
|
|
```python
|
|
config = entropyk.PicardConfig(
|
|
max_iterations=500,
|
|
tolerance=1e-4,
|
|
relaxation=0.5,
|
|
# NEW FIELDS:
|
|
initial_state=[...],
|
|
timeout_ms=10000,
|
|
convergence_criteria=entropyk.ConvergenceCriteria(...)
|
|
)
|
|
```
|
|
|
|
### AC3: SolverStrategy Exposure
|
|
|
|
**Given** Python script using entropyk
|
|
**When** needing uniform solver interface
|
|
**Then** `SolverStrategy` enum is available:
|
|
|
|
```python
|
|
# Create strategy directly
|
|
strategy = entropyk.SolverStrategy.newton(tolerance=1e-6)
|
|
strategy = entropyk.SolverStrategy.picard(relaxation=0.5)
|
|
|
|
# Use default
|
|
strategy = entropyk.SolverStrategy.default()
|
|
|
|
# Solve uniformly
|
|
result = strategy.solve(system)
|
|
```
|
|
|
|
### AC4: Supporting Types
|
|
|
|
**Given** Python script using entropyk
|
|
**When** configuring advanced options
|
|
**Then** supporting types are available:
|
|
|
|
```python
|
|
# JacobianFreezingConfig
|
|
jf_config = entropyk.JacobianFreezingConfig(
|
|
max_frozen_iters=3,
|
|
threshold=0.1
|
|
)
|
|
|
|
# TimeoutConfig
|
|
to_config = entropyk.TimeoutConfig(
|
|
return_best_state_on_timeout=True,
|
|
zoh_fallback=False
|
|
)
|
|
|
|
# ConvergenceCriteria
|
|
cc = entropyk.ConvergenceCriteria(
|
|
pressure_tolerance_pa=1.0,
|
|
mass_balance_tolerance_kgs=1e-9,
|
|
energy_balance_tolerance_w=1e-6
|
|
)
|
|
```
|
|
|
|
### AC5: Backward Compatibility
|
|
|
|
**Given** existing Python code
|
|
**When** upgrading to new version
|
|
**Then** all existing code continues to work:
|
|
|
|
```python
|
|
# OLD CODE - still works
|
|
config = entropyk.NewtonConfig(max_iterations=100, tolerance=1e-6)
|
|
result = config.solve(system)
|
|
|
|
# NEW CODE - with advanced options
|
|
config = entropyk.NewtonConfig(
|
|
max_iterations=100,
|
|
tolerance=1e-6,
|
|
initial_state=previous_result.state_vector
|
|
)
|
|
```
|
|
|
|
## Implementation Tasks
|
|
|
|
### Task 1: Extend PyNewtonConfig
|
|
|
|
- [x] Add `initial_state: Option<Vec<f64>>` field
|
|
- [x] Add `use_numerical_jacobian: bool` field
|
|
- [x] Add `jacobian_freezing: Option<PyJacobianFreezingConfig>` field
|
|
- [x] Add `convergence_criteria: Option<PyConvergenceCriteria>` field
|
|
- [x] Add `timeout_config: PyTimeoutConfig` field
|
|
- [x] Add `previous_state: Option<Vec<f64>>` field
|
|
- [x] Add `line_search_armijo_c: f64` field
|
|
- [x] Add `line_search_max_backtracks: usize` field
|
|
- [x] Add `divergence_threshold: f64` field
|
|
- [x] Update `solve()` to pass all fields to Rust `NewtonConfig`
|
|
|
|
### Task 2: Extend PyPicardConfig
|
|
|
|
- [x] Add `initial_state: Option<Vec<f64>>` field
|
|
- [x] Add `timeout_ms: Option<u64>` field
|
|
- [x] Add `convergence_criteria: Option<PyConvergenceCriteria>` field
|
|
- [x] Update `solve()` to pass all fields to Rust `PicardConfig`
|
|
|
|
### Task 3: Create PySolverStrategy
|
|
|
|
- [x] Create `PySolverStrategy` struct wrapping `SolverStrategy`
|
|
- [x] Implement `#[staticmethod] newton()` constructor
|
|
- [x] Implement `#[staticmethod] picard()` constructor
|
|
- [x] Implement `#[staticmethod] default()` constructor
|
|
- [x] Implement `solve(&mut self, system: &mut PySystem)` method
|
|
- [x] Register in `lib.rs`
|
|
|
|
### Task 4: Create Supporting Types
|
|
|
|
- [x] Create `PyJacobianFreezingConfig` struct
|
|
- [x] Create `PyTimeoutConfig` struct
|
|
- [x] Create `PyConvergenceCriteria` struct
|
|
- [x] Register all in `lib.rs`
|
|
|
|
### Task 5: Update Documentation
|
|
|
|
- [x] Update `bindings/python/README.md` with new API
|
|
- [x] Add examples for warm-start solving
|
|
- [x] Add examples for Jacobian freezing
|
|
- [x] Update `solver_control_examples.ipynb`
|
|
|
|
### Task 6: Add Tests
|
|
|
|
- [x] Test `initial_state` warm-start
|
|
- [x] Test `jacobian_freezing` configuration
|
|
- [x] Test `convergence_criteria` configuration
|
|
- [x] Test `SolverStrategy` usage
|
|
- [x] Test backward compatibility
|
|
|
|
## File List
|
|
|
|
| File | Action | Description |
|
|
|------|--------|-------------|
|
|
| `bindings/python/src/solver.rs` | Modify | Extend config structs |
|
|
| `bindings/python/src/lib.rs` | Modify | Register new classes |
|
|
| `bindings/python/README.md` | Modify | Document new API |
|
|
| `bindings/python/tests/test_solver.py` | Modify | Add tests |
|
|
| `bindings/python/solver_control_examples.ipynb` | Modify | Add examples |
|
|
|
|
## Technical Notes
|
|
|
|
### Priority Implementation Order
|
|
|
|
1. **P0: `initial_state`** - Most critical for convergence
|
|
2. **P0: `SolverStrategy`** - Architectural consistency
|
|
3. **P1: `jacobian_freezing`** - Performance optimization
|
|
4. **P1: `convergence_criteria`** - Multi-circuit support
|
|
5. **P2: Other fields** - Advanced tuning
|
|
|
|
### Memory Safety
|
|
|
|
- `initial_state` and `previous_state` are cloned when passed to Rust
|
|
- No lifetime issues as all data is owned
|
|
- `unsendable` on `PySystem` remains for now (future: thread safety)
|
|
|
|
### Performance Considerations
|
|
|
|
- Jacobian freezing can reduce per-iteration time by ~80%
|
|
- Warm-start can reduce iterations by 50-90% for similar conditions
|
|
|
|
## References
|
|
|
|
- **FR52**: Python Solver Configuration Parity [Source: prd.md]
|
|
- **Story 6.2**: Python Bindings (PyO3) - foundation [Source: epics.md]
|
|
- **Epic 4**: Intelligent Solver Engine - Rust solver implementation [Source: epics.md]
|
|
- **NewtonConfig**: `crates/solver/src/solver.rs:398-490`
|
|
- **PicardConfig**: `crates/solver/src/solver.rs` (Picard section)
|
|
- **SolverStrategy**: `crates/solver/src/solver.rs:2030-2073`
|
|
|
|
## Dependencies
|
|
|
|
- Story 6.2 (Python Bindings) - ✅ Complete
|
|
- Epic 4 (Solver Engine) - ✅ Complete
|
|
|
|
## Definition of Done
|
|
|
|
- [x] All acceptance criteria met
|
|
- [x] All tests pass (`pytest tests/ -v`)
|
|
- [x] Documentation updated
|
|
- [x] Backward compatibility verified
|
|
- [x] Code reviewed
|
|
- [ ] Merged to main
|