RustedSciThe 0.4.20

Rust framework for symbolic and numerical computing:BVP ( Newton-Raphson frozen/damped/with collocations ), IVP( BDF, Radau, Backward Euler, LSODE, LSODA, RK45, DoPri), nonlinear equations ( Levenberg, Gavin) and more
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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
/// Unified callback/metadata handoff for the damped sparse BVP solver.
pub struct DampedGeneratedSolverState {
    /// Residual callback ready for the solver runtime loop.
    pub fun: Box<dyn Fun>,
    /// Jacobian callback ready for the solver runtime loop.
    pub jac: Option<Box<dyn Jac>>,
    /// Per-unknown bounds on the discretized state vector.
    pub bounds_vec: Vec<(f64, f64)>,
    /// Per-unknown relative tolerances on the discretized state vector.
    pub rel_tolerance_vec: Vec<f64>,
    /// Flattened symbolic variable names in solver input order.
    pub variable_string: Vec<String>,
    /// Jacobian bandwidth metadata used by sparse linear solves.
    pub bandwidth: (usize, usize),
    /// Boundary condition positions and values in the flattened state vector.
    pub bc_position_and_value: Vec<(usize, usize, f64)>,
    /// Updated resolver snapshot that includes any newly materialized AOT artifact.
    pub updated_resolver: Option<AotResolver>,
    /// Backend branch that actually supplied the runtime callbacks.
    pub selected_backend: SelectedBackendKind,
    /// Runtime diagnostics for generated callback execution.
    pub runtime_diagnostics: HashMap<String, String>,
    /// Typed symbolic/backend preparation stages captured before the solve.
    pub generation_telemetry: Option<BvpGenerationTelemetrySnapshot>,
    /// Typed Atom-native preparation telemetry, when the selected route used it.
    pub atom_discretization_telemetry:
        Option<crate::symbolic::bvp::telemetry::BvpAtomDiscretizationTelemetrySnapshot>,
    /// Runtime callback telemetry for the ExprLegacy Lambdify route.
    pub legacy_lambdify_telemetry: Option<BvpLambdifyTelemetry>,
    /// Runtime callback telemetry for the AtomView Lambdify route.
    pub atom_lambdify_telemetry: Option<BvpLambdifyTelemetry>,
    /// Immutable snapshot of the direct no-Mutex Banded callback telemetry.
    pub direct_banded_jacobian_telemetry: Option<BvpDirectJacobianTelemetry>,
    /// Shared typed AOT telemetry handle for live post-solve snapshots.
    pub aot_telemetry: Option<BvpAotTelemetry>,
    /// Numeric parameter binding shared by prepared Lambdify callbacks.
    pub(crate) parameter_binding:
        Option<crate::symbolic::bvp::parameter_binding::BvpParameterBindingHandle>,
}

/// Unified callback/metadata handoff for the frozen sparse BVP solver.
pub struct FrozenGeneratedSolverState {
    /// Residual callback ready for the solver runtime loop.
    pub fun: Box<dyn Fun>,
    /// Jacobian callback ready for the solver runtime loop.
    pub jac: Option<Box<dyn Jac>>,
    /// Flattened symbolic variable names in solver input order.
    pub variable_string: Vec<String>,
    /// Jacobian bandwidth metadata used by sparse linear solves.
    pub bandwidth: (usize, usize),
    /// Updated resolver snapshot that includes any newly materialized AOT artifact.
    pub updated_resolver: Option<AotResolver>,
    /// Backend branch that actually supplied the runtime callbacks.
    pub selected_backend: SelectedBackendKind,
    /// Runtime diagnostics for generated callback execution.
    pub runtime_diagnostics: HashMap<String, String>,
    /// Typed symbolic/backend preparation stages captured before the solve.
    pub generation_telemetry: Option<BvpGenerationTelemetrySnapshot>,
    /// Typed Atom-native preparation telemetry, when the selected route used it.
    pub atom_discretization_telemetry:
        Option<crate::symbolic::bvp::telemetry::BvpAtomDiscretizationTelemetrySnapshot>,
    /// Runtime callback telemetry for the ExprLegacy Lambdify route.
    pub legacy_lambdify_telemetry: Option<BvpLambdifyTelemetry>,
    /// Runtime callback telemetry for the AtomView Lambdify route.
    pub atom_lambdify_telemetry: Option<BvpLambdifyTelemetry>,
    /// Immutable snapshot of the direct no-Mutex Banded callback telemetry.
    pub direct_banded_jacobian_telemetry: Option<BvpDirectJacobianTelemetry>,
    /// Numeric parameter binding shared by prepared Lambdify callbacks.
    pub(crate) parameter_binding:
        Option<crate::symbolic::bvp::parameter_binding::BvpParameterBindingHandle>,
}

/// Applies a damped generated handoff state to a solver-specific runtime object.
pub trait ApplyDampedGeneratedSolverState {
    /// Stores callbacks and metadata produced by the generated handoff layer.
    fn apply_generated_solver_state(&mut self, state: DampedGeneratedSolverState);
}

/// Applies a frozen generated handoff state to a solver-specific runtime object.
pub trait ApplyFrozenGeneratedSolverState {
    /// Stores callbacks and metadata produced by the generated handoff layer.
    fn apply_generated_solver_state(&mut self, state: FrozenGeneratedSolverState);
}

/// Builds a complete damped solver handoff request from a solver runtime object.
pub trait BuildDampedSolverRequest {
    /// Creates the symbolic-to-generated request consumed by the shared handoff layer.
    fn build_solver_request(
        &mut self,
        mesh: Option<Vec<f64>>,
        bandwidth: Option<(usize, usize)>,
    ) -> DampedSolverBuildRequest;
}

/// Builds a complete frozen solver handoff request from a solver runtime object.
pub trait BuildFrozenSolverRequest {
    /// Creates the symbolic-to-generated request consumed by the shared handoff layer.
    fn build_solver_request(&self) -> FrozenSolverBuildRequest;
}

/// User-facing configuration for generated backend selection in BVP solvers.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum AotBuildProfile {
    #[default]
    Release,
    Debug,
}

/// Solver-facing rustc/codegen configuration for generated AOT crate builds.
pub type AotCompileConfig = LifecycleAotCompileConfig;

/// Solver-level build policy for generated AOT artifacts.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum AotBuildPolicy {
    #[default]
    UseIfAvailable,
    BuildIfMissing {
        profile: AotBuildProfile,
    },
    RequirePrebuilt,
    RebuildAlways {
        profile: AotBuildProfile,
    },
}

impl AotBuildPolicy {
    /// Short stable label for logging and user-facing diagnostics.
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::UseIfAvailable => "UseIfAvailable",
            Self::BuildIfMissing { .. } => "BuildIfMissing",
            Self::RequirePrebuilt => "RequirePrebuilt",
            Self::RebuildAlways { .. } => "RebuildAlways",
        }
    }
}

/// Solver-level execution policy for compiled AOT callbacks.
#[derive(Clone, Debug, PartialEq, Default)]
pub enum AotExecutionPolicy {
    #[default]
    Auto,
    SequentialOnly,
    Parallel(ParallelExecutorConfig),
}

impl AotExecutionPolicy {
    /// Short stable label for logging and user-facing diagnostics.
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Auto => "Auto",
            Self::SequentialOnly => "SequentialOnly",
            Self::Parallel(_) => "Parallel",
        }
    }
}

/// Optional chunking overrides surfaced at solver setup level.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct AotChunkingPolicy {
    /// Optional residual chunking override.
    pub residual: Option<ResidualChunkingStrategy>,
    /// Optional sparse Jacobian chunking override.
    pub sparse_jacobian: Option<SparseChunkingStrategy>,
}

impl AotChunkingPolicy {
    /// Creates an empty chunking policy that keeps backend defaults.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a policy from explicit residual and sparse Jacobian strategies.
    pub fn with_parts(
        residual: Option<ResidualChunkingStrategy>,
        sparse_jacobian: Option<SparseChunkingStrategy>,
    ) -> Self {
        Self {
            residual,
            sparse_jacobian,
        }
    }
}

/// User-facing configuration for generated backend selection in BVP solvers.
#[derive(Clone, Debug)]
pub struct GeneratedBackendConfig {
    /// Optional explicit backend policy override.
    pub backend_policy_override: Option<BackendSelectionPolicy>,
    /// Optional compiled AOT resolver snapshot.
    pub resolver: Option<AotResolver>,
    /// Solver-level execution policy for compiled AOT callbacks.
    pub aot_execution_policy: AotExecutionPolicy,
    /// Solver-level build policy for generated AOT artifacts.
    pub aot_build_policy: AotBuildPolicy,
    /// Optional compile-time rustc/codegen overrides for generated AOT artifacts.
    pub aot_compile_config: AotCompileConfig,
    /// Codegen backend used to emit generated AOT artifacts.
    pub aot_codegen_backend: AotCodegenBackend,
    /// Optional explicit C compiler for C AOT backends, e.g. `gcc` or `tcc`.
    pub aot_c_compiler: Option<String>,
    /// Optional chunking overrides for residual and sparse Jacobian generation.
    pub aot_chunking_policy: AotChunkingPolicy,
    /// AtomView lowering optimization profile used when materializing AOT artifacts.
    pub atom_optimization_profile: AtomOptimizationProfile,
    /// Symbolic assembly backend used before lambdify/AOT lowering.
    pub symbolic_assembly_backend: BvpSymbolicAssemblyBackend,
    /// Optional matrix backend override for the modern generated BVP path.
    pub matrix_backend_override: Option<MatrixBackend>,
    /// Native linear solver configuration used by the generated banded runtime path.
    pub banded_linear_solver_config: LinearSolverConfig,
    /// Runtime collection mode for Lambdify callback telemetry. This has no
    /// effect on compiled AOT callbacks.
    pub lambdify_telemetry_mode: BvpLambdifyTelemetryMode,
    /// Runtime collection mode for AtomView AOT preparation and generated
    /// callback lifecycle telemetry. `Off` is the zero-overhead default.
    pub aot_telemetry_mode: BvpAotTelemetryMode,
    /// Runtime execution policy for pure Lambdify residual/Jacobian callbacks.
    /// This is independent from AOT execution policy.
    pub lambdify_execution_policy: BvpLambdifyExecutionPolicy,
    /// Opt-in structured decision logging for the BVP solver runtime.
    pub bvp_logging_mode: BvpLoggingMode,
    /// Maximum number of retained typed decision events per solve.
    pub bvp_logging_max_events: usize,
    /// Collection policy for solver counters and stage timings.
    pub bvp_telemetry_mode: BvpTelemetryMode,
}

impl Default for GeneratedBackendConfig {
    fn default() -> Self {
        // The public default is the production BVP route: AtomView for
        // symbolic preparation and tcc for on-demand C AOT.  Rust and
        // ExprLegacy remain explicit compatibility/diagnostic choices.
        Self {
            backend_policy_override: None,
            resolver: None,
            aot_execution_policy: AotExecutionPolicy::Auto,
            aot_build_policy: AotBuildPolicy::UseIfAvailable,
            aot_compile_config: AotCompileConfig::default(),
            aot_codegen_backend: AotCodegenBackend::C,
            aot_c_compiler: Some("tcc".to_string()),
            aot_chunking_policy: AotChunkingPolicy::default(),
            atom_optimization_profile: AtomOptimizationProfile::Full,
            symbolic_assembly_backend: BvpSymbolicAssemblyBackend::AtomView,
            matrix_backend_override: None,
            banded_linear_solver_config: LinearSolverConfig::default(),
            lambdify_telemetry_mode: BvpLambdifyTelemetryMode::Off,
            aot_telemetry_mode: BvpAotTelemetryMode::Off,
            lambdify_execution_policy: BvpLambdifyExecutionPolicy::default(),
            bvp_logging_mode: BvpLoggingMode::Off,
            bvp_logging_max_events: BvpLoggingConfig::default().max_events,
            bvp_telemetry_mode: BvpTelemetryMode::Counters,
        }
    }
}

/// High-level sparse generated-backend modes exposed at solver setup level.
///
/// These modes intentionally hide backend-selection and build-policy details from
/// typical solver users while still allowing advanced callers to drop down to
/// [`GeneratedBackendConfig`] when they need finer control.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum SparseGeneratedBackendMode {
    /// Prefer compiled AOT when available and otherwise fall back to lambdify.
    #[default]
    Defaults,
    /// Require a previously built sparse AOT artifact.
    RequirePrebuilt,
    /// Build a release sparse AOT artifact when it is missing.
    BuildIfMissingRelease,
}

impl SparseGeneratedBackendMode {
    /// Converts the high-level sparse mode into a concrete generated-backend configuration.
    pub fn generated_backend_config(self) -> GeneratedBackendConfig {
        match self {
            Self::Defaults => GeneratedBackendConfig::sparse_defaults(),
            Self::RequirePrebuilt => GeneratedBackendConfig::sparse_require_prebuilt(),
            Self::BuildIfMissingRelease => {
                GeneratedBackendConfig::sparse_build_if_missing_release()
            }
        }
    }
}

/// High-level banded generated-backend modes exposed at solver setup level.
///
/// These presets select the `Banded` matrix backend and route native linear
/// solves to the faithful LAPACK-style banded LU solver with `refine = 0`.
/// Advanced users can still override compiler, chunking, build policy, or the
/// native linear solver through [`GeneratedBackendConfig`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum BandedGeneratedBackendMode {
    /// Prefer compiled AOT when available and otherwise fall back to lambdify.
    #[default]
    Defaults,
    /// Force the lambdify callback path while keeping the native banded matrix
    /// and faithful LAPACK-style linear solver backend.
    Lambdify,
    /// Build a release AOT artifact when it is missing.
    BuildIfMissingRelease,
}

impl BandedGeneratedBackendMode {
    /// Converts the high-level banded mode into a concrete generated-backend configuration.
    pub fn generated_backend_config(self) -> GeneratedBackendConfig {
        match self {
            Self::Defaults => GeneratedBackendConfig::banded_defaults(),
            Self::Lambdify => GeneratedBackendConfig::banded_lambdify_defaults(),
            Self::BuildIfMissingRelease => {
                GeneratedBackendConfig::banded_build_if_missing_release()
            }
        }
    }
}

impl GeneratedBackendConfig {
    /// Creates an empty generated-backend configuration.
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the runtime telemetry mode for Lambdify callbacks.
    pub fn with_lambdify_telemetry_mode(mut self, mode: BvpLambdifyTelemetryMode) -> Self {
        self.lambdify_telemetry_mode = mode;
        self
    }

    /// Sets typed telemetry for the AtomView-native AOT route.
    pub fn with_aot_telemetry_mode(mut self, mode: BvpAotTelemetryMode) -> Self {
        self.aot_telemetry_mode = mode;
        self
    }

    /// Selects how pure Lambdify residual/Jacobian callbacks execute.
    ///
    /// This policy is separate from AOT execution and is applied only when
    /// the selected route is Lambdify. The default preserves the historical
    /// parallel callback behavior.
    pub fn with_lambdify_execution_policy(mut self, policy: BvpLambdifyExecutionPolicy) -> Self {
        self.lambdify_execution_policy = policy;
        self
    }

    /// Enables typed solver decision logging without changing numerical code.
    pub fn with_bvp_logging_mode(mut self, mode: BvpLoggingMode) -> Self {
        self.bvp_logging_mode = mode;
        self
    }

    /// Sets the bounded capacity of the typed decision trace.
    pub fn with_bvp_logging_max_events(mut self, max_events: usize) -> Self {
        self.bvp_logging_max_events = max_events;
        self
    }

    /// Sets the complete structured logging policy.
    pub fn with_bvp_logging_config(mut self, config: BvpLoggingConfig) -> Self {
        self.bvp_logging_mode = config.mode;
        self.bvp_logging_max_events = config.max_events;
        self
    }

    /// Selects the solver telemetry collection policy. `Off` avoids counter
    /// increments, timers, and callback-stage collection on the hot path.
    pub fn with_bvp_telemetry_mode(mut self, mode: BvpTelemetryMode) -> Self {
        self.bvp_telemetry_mode = mode;
        self
    }

    /// Creates the default sparse/BVP generated-backend configuration.
    ///
    /// Practical guidance:
    /// - good general default when you do not want to commit to a specific backend,
    /// - uses `AtomView`, whose sparse-first symbolic Jacobian route removes
    ///   the legacy row-differentiation bottleneck on measured BVP systems,
    /// - still prefers compiled AOT when available,
    /// - otherwise falls back to the established lambdify path.
    pub fn sparse_defaults() -> Self {
        Self::new()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_backend_policy_override(Some(BackendSelectionPolicy::PreferAotThenLambdify))
    }

    /// Creates a sparse generated-backend configuration that requires a prebuilt AOT artifact.
    pub fn sparse_require_prebuilt() -> Self {
        Self::sparse_defaults().with_aot_build_policy(AotBuildPolicy::RequirePrebuilt)
    }

    /// Creates a sparse generated-backend configuration that builds a release artifact on demand.
    ///
    /// Practical guidance:
    /// - best fit for interactive "build on first use" workflows,
    /// - defaults to the fast `DevFastest` compile preset,
    /// - still backend-agnostic until you explicitly choose Rust/C/Zig.
    pub fn sparse_build_if_missing_release() -> Self {
        Self::sparse_defaults()
            .with_aot_build_policy(AotBuildPolicy::BuildIfMissing {
                profile: AotBuildProfile::Release,
            })
            // Build-if-missing is primarily an interactive/on-demand workflow, so default to
            // the fastest practical compile preset instead of the heaviest production codegen.
            .with_aot_compile_dev_fastest()
    }

    /// Creates a generated-backend configuration from a high-level sparse mode.
    pub fn from_sparse_mode(mode: SparseGeneratedBackendMode) -> Self {
        mode.generated_backend_config()
    }

    /// Creates the default banded/BVP generated-backend configuration.
    ///
    /// Practical guidance:
    /// - selects the generated `Banded` matrix path,
    /// - uses faithful LAPACK-style banded LU as the native linear solver,
    /// - uses `AtomView`, whose sparse-first symbolic Jacobian route avoids the
    ///   expensive legacy row-differentiation pass on banded BVP systems,
    /// - keeps `refine = 0` because current BVP workloads do not benefit from
    ///   the extra correction pass,
    /// - prefers compiled AOT when available and falls back to lambdify.
    pub fn banded_defaults() -> Self {
        Self::sparse_defaults()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_matrix_backend_override(MatrixBackend::Banded)
            .with_banded_linear_solver_config(LinearSolverConfig::faithful_banded())
    }

    /// Creates a banded configuration that explicitly uses lambdify callbacks.
    ///
    /// This is the simple callback path: `AtomView` generated `Banded` matrix
    /// assembly plus faithful LAPACK-style native banded solves. Callers that
    /// need compatibility comparison can explicitly override `ExprLegacy`.
    pub fn banded_lambdify_defaults() -> Self {
        Self::banded_defaults()
            .with_backend_policy_override(Some(BackendSelectionPolicy::LambdifyOnly))
    }

    /// Creates a banded configuration that builds a release AOT artifact on demand.
    pub fn banded_build_if_missing_release() -> Self {
        Self::banded_defaults()
            .with_aot_build_policy(AotBuildPolicy::BuildIfMissing {
                profile: AotBuildProfile::Release,
            })
            .with_aot_compile_dev_fastest()
    }

    /// Creates a generated-backend configuration from a high-level banded mode.
    pub fn from_banded_mode(mode: BandedGeneratedBackendMode) -> Self {
        mode.generated_backend_config()
    }

    /// Creates a configuration from explicit policy and resolver values.
    pub fn with_parts(
        backend_policy_override: Option<BackendSelectionPolicy>,
        resolver: Option<AotResolver>,
    ) -> Self {
        Self {
            backend_policy_override,
            resolver,
            aot_execution_policy: AotExecutionPolicy::Auto,
            aot_build_policy: AotBuildPolicy::UseIfAvailable,
            aot_compile_config: AotCompileConfig::default(),
            aot_codegen_backend: AotCodegenBackend::C,
            aot_c_compiler: Some("tcc".to_string()),
            aot_chunking_policy: AotChunkingPolicy::default(),
            atom_optimization_profile: AtomOptimizationProfile::Full,
            symbolic_assembly_backend: BvpSymbolicAssemblyBackend::AtomView,
            matrix_backend_override: None,
            banded_linear_solver_config: crate::somelinalg::banded::LinearSolverConfig::default(),
            lambdify_telemetry_mode: BvpLambdifyTelemetryMode::Off,
            aot_telemetry_mode: BvpAotTelemetryMode::Off,
            lambdify_execution_policy: BvpLambdifyExecutionPolicy::default(),
            bvp_logging_mode: BvpLoggingMode::Off,
            bvp_logging_max_events: BvpLoggingConfig::default().max_events,
            bvp_telemetry_mode: BvpTelemetryMode::Counters,
        }
    }

    /// Sets an explicit backend policy override.
    pub fn with_backend_policy_override(
        mut self,
        backend_policy_override: Option<BackendSelectionPolicy>,
    ) -> Self {
        self.backend_policy_override = backend_policy_override;
        self
    }

    /// Sets an optional compiled AOT resolver snapshot.
    pub fn with_resolver(mut self, resolver: Option<AotResolver>) -> Self {
        self.resolver = resolver;
        self
    }

    /// Sets the AOT execution policy exposed at solver level.
    pub fn with_aot_execution_policy(mut self, policy: AotExecutionPolicy) -> Self {
        self.aot_execution_policy = policy;
        self
    }

    /// Sets the AOT build policy exposed at solver level.
    pub fn with_aot_build_policy(mut self, policy: AotBuildPolicy) -> Self {
        self.aot_build_policy = policy;
        self
    }

    /// Sets compile-time rustc/codegen overrides for generated AOT artifacts.
    pub fn with_aot_compile_config(mut self, config: AotCompileConfig) -> Self {
        self.aot_compile_config = config;
        self
    }

    /// Selects the backend used to emit generated AOT artifacts.
    pub fn with_aot_codegen_backend(mut self, backend: AotCodegenBackend) -> Self {
        self.aot_codegen_backend = backend;
        self
    }

    /// Selects an explicit C compiler for C AOT backends.
    pub fn with_aot_c_compiler(mut self, compiler: impl Into<String>) -> Self {
        self.aot_c_compiler = Some(compiler.into());
        self
    }

    /// Uses AtomView symbolic assembly plus on-demand `gcc`-compiled C AOT.
    ///
    /// Practical guidance:
    /// - strong choice when runtime throughput matters more than bootstrap latency,
    /// - especially useful for repeated solves on the same symbolic problem.
    pub fn sparse_atomview_build_if_missing_release_gcc() -> Self {
        Self::sparse_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::C)
            .with_aot_c_compiler("gcc")
    }

    /// Uses AtomView symbolic assembly plus on-demand `tcc`-compiled C AOT.
    ///
    /// Practical guidance:
    /// - strongest compiled choice for low-latency bootstrap,
    /// - currently the most practical repeated-solve backend once you expect
    ///   roughly `2-3` solves or more on large combustion-style BVPs.
    pub fn sparse_atomview_build_if_missing_release_tcc() -> Self {
        Self::sparse_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::C)
            .with_aot_c_compiler("tcc")
    }

    /// Uses AtomView symbolic assembly plus on-demand Zig-compiled sparse AOT.
    pub fn sparse_atomview_build_if_missing_release_zig() -> Self {
        Self::sparse_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::Zig)
    }

    /// User-facing alias for the practical repeated-solve recommendation:
    /// AtomView symbolic assembly plus `tcc`-compiled C AOT.
    pub fn sparse_atomview_for_repeated_solves() -> Self {
        Self::sparse_atomview_build_if_missing_release_tcc()
    }

    /// Uses AtomView symbolic assembly plus on-demand `gcc`-compiled banded C AOT.
    pub fn banded_atomview_build_if_missing_release_gcc() -> Self {
        Self::banded_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::C)
            .with_aot_c_compiler("gcc")
    }

    /// Uses AtomView symbolic assembly plus on-demand `tcc`-compiled banded C AOT.
    ///
    /// This is usually the quickest compiled bootstrap path for large BVP
    /// experiments when the C toolchain is available.
    pub fn banded_atomview_build_if_missing_release_tcc() -> Self {
        Self::banded_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::C)
            .with_aot_c_compiler("tcc")
    }

    /// Uses AtomView symbolic assembly plus on-demand Zig-compiled banded AOT.
    pub fn banded_atomview_build_if_missing_release_zig() -> Self {
        Self::banded_build_if_missing_release()
            .with_symbolic_assembly_backend(BvpSymbolicAssemblyBackend::AtomView)
            .with_aot_codegen_backend(AotCodegenBackend::Zig)
    }

    /// User-facing alias for the practical repeated-solve banded recommendation:
    /// AtomView symbolic assembly plus `tcc`-compiled C AOT, backed by faithful
    /// LAPACK-style native banded LU.
    pub fn banded_atomview_for_repeated_solves() -> Self {
        Self::banded_atomview_build_if_missing_release_tcc()
    }

    /// Uses the default production-oriented compile settings for generated AOT artifacts.
    pub fn with_aot_compile_production(self) -> Self {
        self.with_aot_compile_config(AotCompileConfig::production())
    }

    /// Uses the faster-build compromise preset for generated AOT artifacts.
    pub fn with_aot_compile_fast_build(self) -> Self {
        self.with_aot_compile_config(AotCompileConfig::fast_build())
    }

    /// Uses the fastest developer-oriented compile preset for generated AOT artifacts.
    pub fn with_aot_compile_dev_fastest(self) -> Self {
        self.with_aot_compile_config(AotCompileConfig::dev_fastest())
    }

    /// Sets solver-level chunking overrides for generated AOT plans.
    pub fn with_aot_chunking_policy(mut self, policy: AotChunkingPolicy) -> Self {
        self.aot_chunking_policy = policy;
        self
    }

    /// Sets the AtomView lowering optimization profile for generated AOT artifacts.
    ///
    /// `Full` preserves the production default. `NoCse` is intended for
    /// correctness/performance comparisons around structural common
    /// subexpression elimination.
    pub fn with_atom_optimization_profile(mut self, profile: AtomOptimizationProfile) -> Self {
        self.atom_optimization_profile = profile;
        self
    }

    /// Sets the symbolic assembly backend used before backend lowering.
    pub fn with_symbolic_assembly_backend(mut self, backend: BvpSymbolicAssemblyBackend) -> Self {
        self.symbolic_assembly_backend = backend;
        self
    }

    /// Overrides the matrix backend used by the generated BVP path.
    ///
    /// This keeps the outer solver method stable (`Sparse` on the user-facing
    /// options surface) while allowing the generated symbolic/codegen stack to
    /// target a different matrix representation such as native `Banded`.
    pub fn with_matrix_backend_override(mut self, backend: MatrixBackend) -> Self {
        self.matrix_backend_override = Some(backend);
        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.banded_linear_solver_config = config;
        self
    }

    /// Resolves the effective legacy method string seen by the symbolic BVP
    /// preparation layer.
    pub fn effective_method(&self, fallback_method: &str) -> String {
        self.matrix_backend_override
            .map(|backend| match backend {
                MatrixBackend::Banded => "Banded",
                MatrixBackend::Dense => "Dense",
                MatrixBackend::CsMat => "Sparse_1",
                // The historical nalgebra `CsMatrix` BVP runtime backend was
                // never completed. Keep old provider-level requests on the
                // production sparse route instead of selecting the removed
                // `Sparse_2` trait path.
                MatrixBackend::CsMatrix => "Sparse",
                MatrixBackend::SparseCol | MatrixBackend::ValuesOnly => "Sparse",
            })
            .unwrap_or(fallback_method)
            .to_string()
    }

    /// Returns the effective backend policy for a given solver method.
    pub fn effective_backend_policy(&self, method: &str) -> BackendSelectionPolicy {
        self.backend_policy_override
            .unwrap_or_else(|| backend_policy_for_method(method))
    }
}