# Руководство по Radau
Это руководство описывает поддерживаемую реализацию Radau в
`src/numerical/Radau/new`. Архивная реализация сохранена только как reference
и не является стандартным production-маршрутом.
## 1. Выбор API
Есть два публичных уровня API.
`UniversalODESolver::radau(...)` — короткий конструктор для общего фасада. Он
использует поддерживаемый Radau фиксированного порядка с symbolic Lambdify,
ExprLegacy и плотным хранением. Это удобный вариант, когда нужно единое API
для нескольких солверов без Radau-специфичных настроек.
Для выбора frontend, layout, continuation, output, telemetry, execution policy
или AOT следует использовать прямой API:
```rust
use RustedSciThe::numerical::Radau::{
RadauConfig, RadauFrontend, RadauMatrixLayout, RadauProblem, RadauSolver,
RadauTelemetryMode,
};
use RustedSciThe::symbolic::symbolic_engine::Expr;
let problem = RadauProblem::new(
vec![Expr::parse_expression("-k*y")],
vec!["y".to_owned()],
"t",
)
.with_parameters(vec!["k".to_owned()])
.with_jacobian(vec![Expr::parse_expression("-k")]);
let config = RadauConfig {
frontend: RadauFrontend::ExprLegacy,
matrix_layout: RadauMatrixLayout::Dense,
telemetry: RadauTelemetryMode::Timings,
t_bound: 1.0,
..RadauConfig::default()
};
let mut solver = RadauSolver::prepare(problem, config)?;
let solution = solver.solve_with_parameters(&[1.0], &[2.0])?;
println!("t={} y={:?}", solution.t, solution.y);
# Ok::<(), Box<dyn std::error::Error>>(())
```
Подготовка отделена от решения и возвращает `Result`. Подготовленную модель
можно переиспользовать для серии значений параметров.
## 2. Выбор символьного frontend
`RadauFrontend::ExprLegacy` оставляет подготовку в legacy-представлении
выражений. Это хороший baseline для небольших задач и независимая reference
ветка для проверки correctness.
`RadauFrontend::AtomViewNative` один раз переводит модель в AtomView и далее
использует native-путь подготовки residual/Jacobian и callback-ов. Для больших
символьных систем это первый маршрут, который стоит проверить, но
универсального победителя нет.
Frontend и runtime выбираются независимо:
- `Lambdify` строит in-process closure без внешнего компилятора;
- `Aot` генерирует, компилирует, линкует и переиспользует native artifact.
Обе ветки используют одно численное ядро Radau. Сравнивать их нужно на одной
задаче, с одинаковыми `y0`, tolerance, layout и output policy.
## 3. Выбор матричного layout
Layout выбирается при подготовке:
```rust
let dense = RadauMatrixLayout::Dense;
let sparse = RadauMatrixLayout::Sparse;
let banded = RadauMatrixLayout::Banded { lower: 1, upper: 1 };
```
`Dense` выбирайте для полностью связанных Jacobian и небольших или средних
систем. `Banded` подходит для устойчивой узкой полосы. `Sparse` подходит для
больших задач со стабильным sparse-pattern, если экономия на хранении и
линейной алгебре окупает служебные расходы.
Серебряной пули нет. Diffusion-chain часто выигрывает от Banded или Sparse,
тогда как небольшая combustion или полностью связанная kinetics-задача может
быть быстрее с Dense. Нельзя выбирать structured layout только потому, что
сам residual дешёвый.
После подготовки backend не должен скрытно конвертировать Dense, Sparse и
Banded на каждой scalar-операции: выбранный layout владеет своим callback и
linear workspace.
## 4. Контракт Jacobian
Для symbolic Radau по возможности задавайте явный Jacobian. Вектор имеет
row-major порядок. Для `n` уравнений и `n` переменных идут сначала все
производные первой строки, затем второй и так далее:
```rust
let problem = RadauProblem::new(
vec![Expr::parse_expression("-k*y")],
vec!["y".to_owned()],
"t",
)
.with_parameters(vec!["k".to_owned()])
.with_jacobian(vec![Expr::parse_expression("-k")]);
```
`RadauJacobianSource::Analytic` — default и рекомендуемый вариант для stiff
production-задач. `FiniteDifference` удобен для диагностики и fallback, но
добавляет вызовы residual. Для AOT и structured layout сначала проверяйте
явный Jacobian: так контракт генерации и проверка pattern остаются
однозначными.
### Native callback API
Если модель уже реализована как Rust-замыкания, используйте
`RadauNativeSolver`. Обязателен callback невязки. Если передан callback
Якобиана, используется аналитический путь. Если передать `None`, Radau
использует типизированный конечно-разностный fallback и повторно вызывает
невязку для построения столбцов Якобиана.
Этот native callback API сейчас поддерживает только плотный (`Dense`)
Якобиан. Для `Sparse` и `Banded` следует использовать symbolic API
`RadauProblem`: выбранный structured backend сохраняется без скрытой
конвертации плотного callback во время решения.
При включённой telemetry число внутренних проб конечно-разностного метода
доступно в счётчике `finite_difference_probes`. Это внутренние вызовы для FD,
а не дополнительные пользовательские вызовы solve-level residual callback.
Пример `examples/radau_native_callbacks.rs` показывает оба режима.
## 5. Tolerance и шаги
Главные численные параметры задаются так:
```rust
let config = RadauConfig {
rtol: 1e-6,
atol: 1e-9,
first_step: None,
max_step: f64::INFINITY,
max_steps: 1_000_000,
max_newton_iterations: 6,
max_retries: 12,
..RadauConfig::default()
};
```
Начинайте с default и затем ужесточайте `rtol` и `atol` вместе с проверкой,
масштабированной под задачу. `max_step` задавайте при известном физическом
масштабе или быстро меняющемся forcing. `first_step` нужен только если
автоматическая оценка начального шага не подходит. Увеличение Newton/retry
budget не должно маскировать плохой Jacobian или плохое масштабирование.
## 6. Одиночное решение и continuation
Для модели без параметров:
```rust
let solution = solver.solve(&[1.0])?;
```
Если модель объявлена через `with_parameters`, значения связываются явно:
```rust
for k in [0.5, 1.0, 2.0, 4.0] {
let solution = solver.continue_with_parameters(&[1.0], &[k])?;
println!("k={k}: y(1)={:.9e}", solution.y[0]);
}
```
Continuation переиспользует подготовленную symbolic model: изменение значений
параметров не должно повторять symbolic differentiation и frontend preparation.
Первый solve оплачивает подготовку, следующие solves показывают warm path. В
benchmark подготовку нужно показывать отдельно от повторной серии.
Для нового интервала с сохранением подготовленных callback-ов:
```rust
solver.restart(0.0, 2.0)?;
let solution = solver.continue_with_parameters(&[1.0], &[2.0])?;
```
`restart` меняет только интервал, а новый `y0` передаётся следующему solve.
## 7. Output и post-processing
Политика хранения результата задаётся явно:
```rust
use RustedSciThe::numerical::Radau::RadauOutputPolicy;
let final_only = RadauOutputPolicy::FinalOnly;
let dense_output = RadauOutputPolicy::Dense;
let selected_times = RadauOutputPolicy::Sampled(vec![0.25, 0.5, 0.75, 1.0]);
```
`FinalOnly` требует минимум памяти. `Dense` сохраняет continuous-output
segments и позволяет вызвать `solution.sample(t)` или
`solution.sample_many(×)`. `Sampled` вычисляет только запрошенные точки.
Для большой production-задачи не включайте Dense без необходимости.
## 8. Telemetry
Telemetry по умолчанию выключена и не должна оплачивать работу обычного пути:
```rust
let config = RadauConfig {
telemetry: RadauTelemetryMode::Counters,
..RadauConfig::default()
};
```
`Counters` нужны для числа residual/Jacobian calls, accepted/rejected steps,
Newton iterations, factorizations и linear solves. `Timings` включайте для
расследования performance. Preparation и runtime scopes разделены; parent и
child timings диагностические и не должны складываться.
Важные группы telemetry: preparation, residual/Jacobian, Newton, linear
factorization/solve, output, parameter rebind и AOT build/link/publication.
В отчёте рядом с числами всегда фиксируйте frontend, execution, layout,
output policy и фазу parameter series.
## 9. AOT lifecycle
AOT выбирается явно и требует конфигурации:
```rust
use RustedSciThe::numerical::Radau::{RadauAotConfig, RadauExecution};
let config = RadauConfig {
execution: RadauExecution::Aot,
frontend: RadauFrontend::AtomViewNative,
matrix_layout: RadauMatrixLayout::Dense,
aot: Some(RadauAotConfig::build_if_missing_release(
"target/radau-artifacts",
)),
..RadauConfig::default()
};
```
Режимы:
- `build_if_missing_release(path)` — собрать только при отсутствии artifact;
- `require_prebuilt()` — не запускать compiler и завершиться ошибкой без artifact;
- `rebuild_always_release(path)` — принудительно пересобрать для cold-gate.
Для разработки используйте BuildIfMissing, для deployment и process-isolated
consumer — RequirePrebuilt, а RebuildAlways оставляйте для cold measurement.
Build/link время нельзя смешивать с warm solve временем.
## 10. Sequential, Parallel и Auto
Безопасный default — последовательное выполнение:
```rust
use RustedSciThe::numerical::Radau::RadauExecutionPolicy;
let sequential = RadauExecutionPolicy::Sequential;
let forced = RadauExecutionPolicy::Parallel { min_work: 256 };
let automatic = RadauExecutionPolicy::Auto { min_work: 256 };
```
`Parallel` включайте после измерения workload. `Auto` должен избегать dispatch,
если стоимость worker overhead не окупается. Callback-only и full-solve
break-even нужно измерять отдельно: выигрыш callback может исчезнуть в linear
algebra или controller overhead.
## 11. Ошибки и готовые examples
Preparation и solve возвращают `Result<_, RadauError>`. В прикладном коде
проверяйте тип ошибки, а не разбирайте display-строку. Отдельно различаются
ошибки configuration, shape, callback, unsupported route, Newton failure,
step-budget exhaustion и output range.
Готовые examples:
- `examples/radau_lambdify_single.rs` — одиночный Lambdify solve;
- `examples/radau_lambdify_continuation.rs` — continuation одной prepared model;
- `examples/radau_public_api_guide.rs` — AtomView, counters и dense output;
- `examples/radau_backends_guide.rs` — сравнение frontend/layout;
- `examples/radau_aot_single.rs` и `examples/radau_aot_continuation.rs` — AOT;
- `examples/universal_ode_example.rs` и `examples/rus/universal_ode_example.rs` —
короткий universal facade.
Сначала подтверждайте correctness на одной и той же задаче. Затем отдельно
сравнивайте preparation, callback-only, full solve и continuation. Быстрый
результат на одном layout не является общим утверждением о Radau.