RustedSciThe 0.4.21

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
# Руководство по 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(&times)`. `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.

## 12. Текстовые task documents

Текстовый маршрут — компактный воспроизводимый адаптер поддерживаемого Radau
для examples и batch-запусков. Если нужны полный выбор frontend, layout, AOT
или настройка parallel executor, используйте нативные `RadauConfig` и
`RadauProblem`.

```text
task
solver: IVP
method: Radau5

equations
arg: t
parameters: k
parameter_values: 2.0
y: -k*y

initial_conditions
t0: 0.0
t_end: 1.0
y0: 1.0

solver_options
rtol: 1e-6
atol: 1e-8
max_step: 0.05
first_step: 1e-3

postprocessing
output_policy: none
```

Парсер направляет `solver: IVP` вместе с `method: Radau5` в native Radau
adapter. `rtol` и `atol` независимы; `tolerance` — удобное общее значение,
если оба допуска должны совпадать. `max_step`, `first_step` и лимиты итераций/
шагов проверяются до создания solver-а. `Radau3` не является поддержанным
методом task parser: текущая реализация использует фиксированный Radau IIA
пятого порядка.

В task documents сейчас доступен компактный symbolic adapter с его
поддерживаемыми настройками по умолчанию. Выбор `ExprLegacy` или
`AtomViewNative`, `Dense`/`Sparse`/`Banded`, AOT build policy, telemetry и
кастомной политики `Parallel`/`Auto` выполняется через типизированный Radau
API. Так advanced option не может быть молча проигнорирована текстовым
форматом.

Continuation переиспользует распарсенную символьную модель, если это допускает
native contract:

```text
continuation
parameter: k
values: 1.0, 2.0, 4.0
mode: prepared
restart_each: false
```

`fresh` заново подготавливает каждый сегмент, `warm` переиспользует callbacks с
предыдущим принятым состоянием, а `prepared` запрашивает максимально сильный
маршрут reuse. Намеренный restart может задавать для сегмента собственные
`y0`, `t0` и `t_end`. В отчётах разделяйте preparation, callback, full-solve и
continuation timings. `output_policy` принимает `none`, `terminal`, `plotters`
или `gnuplot`; `plot` оставлен только как compatibility alias. Комментарии с
`#` игнорируются. Общая грамматика и batch-runner описаны в
[`TASK_DOCS_GUIDE_EN.md`](../../command_interpreter/TASK_DOCS_GUIDE_EN.md).

## 13. Сверка с SciPy и измерения производительности

Численное ядро проверяется по официальной реализации Radau из SciPy
`v1.18.1`. Минимальный набор исходников и сведения об их происхождении
находятся в `src/numerical/ivp_scipy_reference`; в parity-отчётах сохраняются
версии Python, NumPy и SciPy, а также хеши исходников. Сопоставимые проверки
охватывают stiff scalar, Robertson, combustion-like и HIRES, конечное решение
и общие точки dense output, трассы контроллера, обратное интегрирование,
автоматический и заданный `first_step`, некорректную конфигурацию, underflow и
исчерпание бюджета шагов. Дополнительно есть проверки по точным решениям и
инвариантам, а Robertson независимо сверяется с LSODE2/BDF.

Цель — faithful-поведение математического метода и контроллера, а не
побитовое совпадение каждого счётчика или детали реализации. Поэтапно
проверяются Radau IIA пятого порядка, преобразованная collocation-система,
modified Newton, оценка локальной ошибки, адаптивный контроллер и
полином dense output. Оставшиеся наблюдаемые различия объяснены:

- Rust лениво готовит callbacks при решении, тогда как SciPy вычисляет
  начальные RHS/Jacobian при создании solver-а. Это различие жизненного цикла
  API.
- Rust переиспользует буферы workspace и не нуждается в дополнительном
  вычислении SciPy для конечного RHS; поэтому число residual-вызовов может
  быть на один меньше.
- Реализации плотного LU различаются: Rust использует собственный in-place
  real/complex backend, SciPy — LAPACK. Вблизи порога сходимости Newton это
  может изменить решение о пересчёте Jacobian или повторе шага, как в базовом
  Robertson-сценарии, при этом траектория остаётся в заданном допуске.
  Проверка с соседним tolerance подтверждает, что ветвление зависит от
  численной границы.
- При step underflow Rust возвращает типизированную ошибку до бесперспективной
  Newton-попытки; SciPy может выполнить и отклонить одну такую попытку раньше.
  Оба варианта завершаются без принятых шагов. Бюджет принятых шагов Rust —
  расширение API; для сверки ему соответствует явно обозначенный внешний
  лимит driver-а SciPy.

Это осознанные различия жизненного цикла, backend-а и API, а не случайные
отклонения численного алгоритма. Подробная fidelity-матрица и компактные
результаты находятся в `src/numerical/ivp_scipy_reference/TODO.md` и
`src/numerical/Radau/RADAU_STORY_TESTS.md`.

### Снимок производительности численного ядра

В одном release-эксперименте сравнивались решение Rust Radau с заранее
подготовленными native callbacks и создание solver-а SciPy вместе с решением
и вычислением тех же трёх точек результата. Использовались одинаковые
уравнения, аналитические Jacobian, интервалы, tolerance и ограничения шага;
телеметрия была отключена. Полученные отношения времени SciPy/Rust:

| Задача | Решение Rust, медиана | Измеренный scope SciPy, медиана | Отношение |
|---|---:|---:|---:|
| Stiff scalar | 0.0065 ms | 0.4633 ms | 71.28x |
| Robertson | 0.0216 ms | 2.0500 ms | 94.91x |
| Combustion-like | 0.0073 ms | 0.4982 ms | 68.25x |

Это веское свидетельство высокой скорости численного пути Rust на этих
показательных малых stiff-задачах, но не универсальное обещание ускорения.
Измеренные scopes не полностью одинаковы: Rust не включает уже завершённую
подготовку callbacks, тогда как измерение Python включает создание solver-а.
Кроме того, проверены только три задачи на одной машине; другие уравнения,
размеры, layout и окружения могут дать иные результаты. Исходный отчёт:
`test_reports/Radau_SciPy_Parity/release/numerical__Radau__tests__scipy_parity__radau_scipy_release_core_timing_benchmark_story.md`.