1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/// User-facing setup options for the frozen BVP solver.
#[derive(Clone)]
pub struct FrozenSolverOptions {
/// Residual discretization scheme name.
pub scheme: String,
/// Nonlinear solver strategy name.
pub strategy: String,
/// Optional strategy-specific parameters.
pub strategy_params: Option<HashMap<String, Option<Vec<f64>>>>,
/// Optional linear-system method override.
pub linear_sys_method: Option<String>,
/// Matrix backend/method selector.
pub method: String,
/// Absolute convergence tolerance.
pub tolerance: f64,
/// Maximum nonlinear iterations.
pub max_iterations: usize,
/// Generated-backend configuration used by sparse solver paths.
pub generated_backend_config: GeneratedBackendConfig,
}
impl FrozenSolverOptions {
/// Creates frozen solver options from explicit values.
pub fn new(
strategy: String,
strategy_params: Option<HashMap<String, Option<Vec<f64>>>>,
linear_sys_method: Option<String>,
method: String,
tolerance: f64,
max_iterations: usize,
) -> Self {
Self {
scheme: default_forward_scheme_name(),
strategy,
strategy_params,
linear_sys_method,
method,
tolerance,
max_iterations,
generated_backend_config: GeneratedBackendConfig::default(),
}
}
/// Attaches an explicit generated-backend configuration.
pub fn with_generated_backend_config(mut self, config: GeneratedBackendConfig) -> Self {
self.generated_backend_config = config;
self
}
/// Selects the runtime telemetry level for Lambdify callbacks.
///
/// The default is [`BvpLambdifyTelemetryMode::Off`]. Use `Counters` for
/// cheap call counts or `Detailed` when callback wall-clock timings are
/// needed for a diagnostic/story run.
pub fn with_lambdify_telemetry_mode(mut self, mode: BvpLambdifyTelemetryMode) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_lambdify_telemetry_mode(mode);
self
}
/// Selects typed solver decision logging for diagnostic runs.
pub fn with_bvp_logging_mode(mut self, mode: BvpLoggingMode) -> Self {
self.generated_backend_config = self.generated_backend_config.with_bvp_logging_mode(mode);
self
}
/// Configures bounded typed decision logging for the solver runtime.
pub fn with_bvp_logging_config(mut self, config: BvpLoggingConfig) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_bvp_logging_config(config);
self
}
/// Selects whether solver counters and stage timers are collected.
pub fn with_bvp_telemetry_mode(mut self, mode: BvpTelemetryMode) -> Self {
self.generated_backend_config = self.generated_backend_config.with_bvp_telemetry_mode(mode);
self
}
/// Returns options with an explicit AtomView AOT optimization profile.
///
/// The default is `AtomOptimizationProfile::Full`, which preserves the
/// historical CSE-enabled pipeline. Diagnostic profiles such as `NoCse`
/// are useful for correctness and performance A/B checks.
pub fn with_atom_optimization_profile(mut self, profile: AtomOptimizationProfile) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_atom_optimization_profile(profile);
self
}
/// Selects the residual discretization scheme with a typed public API.
///
/// The stored value is still the legacy string consumed by the symbolic
/// discretization layer, but user code should prefer this method over raw
/// `"forward"` / `"trapezoid"` strings.
pub fn with_scheme(mut self, scheme: BvpDerivativeScheme) -> Self {
self.scheme = scheme.as_legacy_str().to_string();
self
}
/// Selects the legacy forward derivative discretization.
pub fn forward_derivative(self) -> Self {
self.with_scheme(BvpDerivativeScheme::Forward)
}
/// Selects the trapezoid derivative discretization.
pub fn trapezoid_derivative(self) -> Self {
self.with_scheme(BvpDerivativeScheme::Trapezoid)
}
/// Compatibility escape hatch for legacy/custom scheme strings.
pub fn with_scheme_name(mut self, scheme: impl Into<String>) -> Self {
self.scheme = scheme.into();
self
}
/// Overrides the native linear solver configuration used by generated banded callbacks.
pub fn with_banded_linear_solver_config(mut self, config: LinearSolverConfig) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_banded_linear_solver_config(config);
self
}
/// Overrides detailed nonlinear strategy parameters.
pub fn with_strategy_params(
mut self,
strategy_params: Option<HashMap<String, Option<Vec<f64>>>>,
) -> Self {
self.strategy_params = strategy_params;
self
}
/// Overrides the absolute convergence tolerance.
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.tolerance = tolerance;
self
}
/// Overrides the nonlinear iteration limit.
pub fn with_max_iterations(mut self, max_iterations: usize) -> Self {
self.max_iterations = max_iterations;
self
}
/// Overrides the linear-system method.
pub fn with_linear_sys_method(mut self, linear_sys_method: Option<String>) -> Self {
self.linear_sys_method = linear_sys_method;
self
}
/// Attaches a high-level sparse generated-backend mode.
pub fn with_sparse_generated_backend_mode(mut self, mode: SparseGeneratedBackendMode) -> Self {
self.generated_backend_config = GeneratedBackendConfig::from_sparse_mode(mode);
self
}
/// Attaches a high-level banded generated-backend mode.
///
/// Banded modes route generated callbacks through native `Banded` matrix
/// assembly and faithful LAPACK-style banded LU with `refine = 0`.
pub fn with_banded_generated_backend_mode(mut self, mode: BandedGeneratedBackendMode) -> Self {
self.generated_backend_config = GeneratedBackendConfig::from_banded_mode(mode);
self
}
/// Selects the symbolic assembly backend used before lambdify/AOT lowering.
pub fn with_symbolic_assembly_backend(mut self, backend: BvpSymbolicAssemblyBackend) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_symbolic_assembly_backend(backend);
self
}
/// Selects the matrix backend through the typed API.
///
/// The historical `method: String` field is retained for compatibility,
/// while the normalized generated-backend configuration becomes the source
/// of truth for new callers.
pub fn with_matrix_backend(mut self, backend: MatrixBackend) -> Self {
self.generated_backend_config = self
.generated_backend_config
.with_matrix_backend_override(backend);
self
}
/// Creates production-oriented sparse frozen solver options with standard defaults.
///
/// This is the preferred starting point for most sparse/frozen BVP users.
pub fn sparse_frozen() -> Self {
Self::default().with_sparse_generated_backend_mode(SparseGeneratedBackendMode::Defaults)
}
/// Creates production-oriented banded frozen solver options with standard defaults.
///
/// This selects generated `Banded` matrix callbacks and faithful
/// LAPACK-style native banded LU (`refine = 0`) while preserving the frozen
/// Newton strategy.
pub fn banded_frozen() -> Self {
Self::default().with_banded_generated_backend_mode(BandedGeneratedBackendMode::Defaults)
}
/// Creates production-oriented dense frozen solver options with standard defaults.
pub fn dense_frozen() -> Self {
Self {
method: default_dense_method_name(),
..Self::default()
}
}
/// Creates production-oriented dense solver options for the naive strategy.
pub fn dense_naive() -> Self {
Self {
strategy: "Naive".to_string(),
strategy_params: None,
..Self::dense_frozen()
}
}
/// Uses the standard sparse generated-backend defaults.
pub fn with_sparse_generated_backend_defaults(self) -> Self {
self.with_sparse_generated_backend_mode(SparseGeneratedBackendMode::Defaults)
}
/// Uses the standard banded generated-backend defaults.
pub fn with_banded_generated_backend_defaults(self) -> Self {
self.with_banded_generated_backend_mode(BandedGeneratedBackendMode::Defaults)
}
/// Uses lambdify callbacks with generated `Banded` matrix assembly.
pub fn with_banded_lambdify(self) -> Self {
self.with_banded_generated_backend_mode(BandedGeneratedBackendMode::Lambdify)
}
/// Builds a banded release AOT backend on demand.
pub fn with_banded_aot_build_if_missing_release(self) -> Self {
self.with_banded_generated_backend_mode(BandedGeneratedBackendMode::BuildIfMissingRelease)
}
/// Requires a prebuilt sparse generated backend.
pub fn with_sparse_aot_require_prebuilt(self) -> Self {
self.with_sparse_generated_backend_mode(SparseGeneratedBackendMode::RequirePrebuilt)
}
/// Builds a sparse release AOT backend on demand.
pub fn with_sparse_aot_build_if_missing_release(self) -> Self {
self.with_sparse_generated_backend_mode(SparseGeneratedBackendMode::BuildIfMissingRelease)
}
/// Uses AtomView symbolic assembly plus on-demand `gcc`-compiled sparse C AOT.
///
/// Prefer this when runtime throughput matters more than startup latency.
pub fn with_sparse_atomview_c_gcc(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::sparse_atomview_build_if_missing_release_gcc(),
)
}
/// Uses AtomView symbolic assembly plus on-demand `tcc`-compiled sparse C AOT.
///
/// Prefer this for practical repeated-solve workflows on the same large BVP.
pub fn with_sparse_atomview_c_tcc(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::sparse_atomview_build_if_missing_release_tcc(),
)
}
/// User-facing alias for the currently recommended repeated-solve compiled path.
pub fn with_sparse_atomview_for_repeated_solves(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::sparse_atomview_for_repeated_solves(),
)
}
/// Uses AtomView symbolic assembly plus on-demand `gcc`-compiled banded C AOT.
pub fn with_banded_atomview_c_gcc(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::banded_atomview_build_if_missing_release_gcc(),
)
}
/// Uses AtomView symbolic assembly plus on-demand `tcc`-compiled banded C AOT.
pub fn with_banded_atomview_c_tcc(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::banded_atomview_build_if_missing_release_tcc(),
)
}
/// Uses AtomView symbolic assembly plus on-demand Zig-compiled banded AOT.
pub fn with_banded_atomview_zig(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::banded_atomview_build_if_missing_release_zig(),
)
}
/// User-facing alias for the currently recommended repeated-solve banded path.
pub fn with_banded_atomview_for_repeated_solves(self) -> Self {
self.with_generated_backend_config(
GeneratedBackendConfig::banded_atomview_for_repeated_solves(),
)
}
}
impl Default for FrozenSolverOptions {
fn default() -> Self {
Self {
scheme: default_forward_scheme_name(),
strategy: "Frozen".to_string(),
strategy_params: Some(HashMap::from([("Frozen_naive".to_string(), None)])),
linear_sys_method: None,
method: default_sparse_method_name(),
tolerance: 1e-6,
max_iterations: DEFAULT_MAX_ITERATIONS,
generated_backend_config: GeneratedBackendConfig::default(),
}
}
}