Skip to main content

logicaffeine_base/
nested_flow.rs

1//! Flows across infinitesimals: the state a loop stepped by one infinitesimal leaves when its affine
2//! update holds values of others.
3//!
4//! **Rates of larger infinitesimals.** `N = T/s + δ` steps of `x ↦ A·x`, `A = I + Σ_{k≥1} u^k·A_k` with
5//! `u = s^(1/q)` and every `A_k` a matrix of values of the infinitesimals larger than `s`, leave
6//! `exp(N·log A)·x₀` — the recurrences of one infinitesimal's flow ([`crate::germ`]) over those values:
7//! `log A = Σ u^k·L_k`, the exponent `C_0 + Σ_{j≥1} u^j·C_j` with `C_j = T·L_{q+j} + δ·L_j`, `C_0 = T·A_q`,
8//! and with commuting terms `exp(Σ u^j·C_j) = Σ u^m·E_m`, `E_m = (1/m)·Σ k·C_k·E_{m−k}`. The standard
9//! part relative to the larger infinitesimals is `e^{T·A_q}·x₀`, the inner limit. Without commuting
10//! terms, the orders below the first nonzero `C_j` are known, and the rest refused by name.
11//!
12//! **The exponential of a matrix of non-standard rates** (`matrix_exp`) is read one infinitesimal at a
13//! time: with `A(ε) = Σ ε^j·B_j` in the smallest infinitesimal its entries hold, the block upper-
14//! triangular Toeplitz matrix of `B_0, …, B_m` has as exponential the Toeplitz matrix of the first
15//! `m + 1` coefficients of `e^{T·A(ε)}` (Van Loan), so each coefficient is the corner block of the
16//! exponential of a matrix of values of larger infinitesimals — down to rational matrices, exactly.
17//!
18//! **Rates of smaller infinitesimals.** `N` steps of `A = I + Σ_{k≥1} w^k·A_k`, `w` a root of an
19//! infinitesimal smaller than `s` and the `A_k` values of `s` and the larger ones, leave
20//! `exp(N·log A)·x₀`, the series `Σ_m w^m·P_m` with `P_m = Σ_{j ≤ m} (N^j/j!)·[(log A)^j]_m`: every
21//! coefficient exact, `N` an ordinary number from the smaller infinitesimal's standpoint. A scalar
22//! update not the identity at order `0` is `exp(N·ln A)`, the germs' own exponential and logarithm.
23
24use std::collections::BTreeMap;
25use std::sync::{Arc, Mutex};
26
27use crate::expansion::{Coefficient, Expansion, ExpansionError, Source};
28use crate::matrix_exp::exp_matrix;
29use crate::nested::{GermRecipe, NestedGerm};
30use crate::number::Number;
31use crate::numeric::Rational;
32use crate::tower::{self, Op, Scale, TowerValue};
33
34/// A matrix of numbers.
35pub(crate) type NMatrix = Vec<Vec<Number>>;
36
37fn text(e: ExpansionError) -> String {
38    e.to_string()
39}
40
41fn zeros(n: usize) -> NMatrix {
42    vec![vec![Number::zero(); n]; n]
43}
44
45fn identity(n: usize) -> NMatrix {
46    (0..n)
47        .map(|i| (0..n).map(|j| if i == j { Number::one() } else { Number::zero() }).collect())
48        .collect()
49}
50
51fn mat_mul(a: &NMatrix, b: &NMatrix) -> Result<NMatrix, String> {
52    let n = a.len();
53    let mut out = zeros(n);
54    for (i, row) in a.iter().enumerate() {
55        for (k, x) in row.iter().enumerate() {
56            if x.is_zero().map_err(text)? {
57                continue;
58            }
59            for j in 0..n {
60                out[i][j] = out[i][j].add(&x.mul(&b[k][j]).map_err(text)?).map_err(text)?;
61            }
62        }
63    }
64    Ok(out)
65}
66
67fn mat_add(a: &NMatrix, b: &NMatrix) -> Result<NMatrix, String> {
68    a.iter()
69        .zip(b)
70        .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.add(q).map_err(text)).collect())
71        .collect()
72}
73
74fn mat_scale(a: &NMatrix, k: &Number) -> Result<NMatrix, String> {
75    a.iter()
76        .map(|row| row.iter().map(|v| v.mul(k).map_err(text)).collect())
77        .collect()
78}
79
80fn is_zero_matrix(a: &NMatrix) -> Result<bool, String> {
81    for row in a {
82        for v in row {
83            if !v.is_zero().map_err(text)? {
84                return Ok(false);
85            }
86        }
87    }
88    Ok(true)
89}
90
91fn rational(r: Rational) -> Number {
92    <Number as TowerValue>::from_rational(r)
93}
94
95fn mat_vec(a: &NMatrix, x: &[Number]) -> Result<Vec<Number>, String> {
96    a.iter()
97        .map(|row| {
98            row.iter().zip(x).try_fold(Number::zero(), |acc, (e, v)| {
99                acc.add(&e.mul(v).map_err(text)?).map_err(text)
100            })
101        })
102        .collect()
103}
104
105fn lcm(a: u32, b: u32) -> u32 {
106    fn gcd(a: u64, b: u64) -> u64 {
107        if b == 0 {
108            a
109        } else {
110            gcd(b, a % b)
111        }
112    }
113    let g = gcd(u64::from(a), u64::from(b));
114    u32::try_from(u64::from(a) / g * u64::from(b)).expect("a grid of moderate index")
115}
116
117/// The smallest infinitesimal any of the numbers involves.
118fn smallest_among<'a>(numbers: impl Iterator<Item = &'a Number>) -> Option<Arc<Scale>> {
119    let mut smallest: Option<Arc<Scale>> = None;
120    for n in numbers {
121        for s in tower::number_scales(n) {
122            smallest = Some(match smallest {
123                None => s,
124                Some(t) if s.order(&t) == Some(std::cmp::Ordering::Less) => s,
125                Some(t) => t,
126            });
127        }
128    }
129    smallest
130}
131
132/// The expansions of a matrix's entries at `scale`, on the least grid all lie on, with that grid.
133fn expansions(a: &NMatrix, scale: &Arc<Scale>) -> Result<(Vec<Vec<Expansion<Number>>>, u32), String> {
134    let mut grid = 1;
135    let mut out = Vec::with_capacity(a.len());
136    for row in a {
137        let mut r = Vec::with_capacity(row.len());
138        for v in row {
139            let e = tower::number_germ(v, scale)?.expansion.clone();
140            grid = lcm(grid, e.ramification());
141            r.push(e);
142        }
143        out.push(r);
144    }
145    let out = out
146        .into_iter()
147        .map(|row| row.into_iter().map(|e| e.refined(grid / e.ramification())).collect())
148        .collect();
149    Ok((out, grid))
150}
151
152/// The matrix of order `k` of a matrix of expansions on one grid.
153fn term(e: &[Vec<Expansion<Number>>], k: i64) -> Result<NMatrix, String> {
154    e.iter()
155        .map(|row| row.iter().map(|x| x.coefficient(k).map_err(text)).collect())
156        .collect()
157}
158
159/// `L_k`, the coefficient of `u^k` in `log A = Σ_{n≥1} (−1)^{n+1}·X^n/n`, `X = Σ_{k≥1} u^k·A_k`.
160fn log_term(terms: &dyn Fn(usize) -> Result<NMatrix, String>, n: usize, k: usize) -> Result<NMatrix, String> {
161    let mut total = zeros(n);
162    let mut power: Vec<NMatrix> = (0..=k).map(|j| if j == 0 { identity(n) } else { zeros(n) }).collect();
163    for count in 1..=k {
164        let mut next: Vec<NMatrix> = vec![zeros(n); k + 1];
165        for (have, p) in power.iter().enumerate() {
166            if is_zero_matrix(p)? {
167                continue;
168            }
169            for step in 1..=(k - have) {
170                next[have + step] = mat_add(&next[have + step], &mat_mul(p, &terms(step)?)?)?;
171            }
172        }
173        power = next;
174        let sign = if count % 2 == 1 { 1 } else { -1 };
175        let c = rational(Rational::from_ratio_i64(sign, count as i64).expect("count ≥ 1"));
176        total = mat_add(&total, &mat_scale(&power[k], &c)?)?;
177    }
178    Ok(total)
179}
180
181// ── the exponential of a matrix of numbers ───────────────────────────────────────────────────
182
183/// The data of `e^{t·A}`, shared by its entries' expansions.
184#[derive(Debug)]
185pub(crate) struct MatrixExpSpec {
186    pub(crate) a: NMatrix,
187    pub(crate) t: Rational,
188    scale: Arc<Scale>,
189    entries: Vec<Vec<Expansion<Number>>>,
190    /// The coefficient matrices of `e^{t·A(ε)}` found so far.
191    found: Mutex<Vec<NMatrix>>,
192}
193
194impl MatrixExpSpec {
195    /// The coefficient of `ε^m` in `e^{t·A(ε)}`: the corner block of the exponential of the block
196    /// Toeplitz matrix of `B_0, …, B_m`.
197    fn coefficient(&self, m: usize) -> Result<NMatrix, String> {
198        if let Some(found) = self.found.lock().expect("unpoisoned").get(m) {
199            return Ok(found.clone());
200        }
201        let n = self.a.len();
202        let blocks: Vec<NMatrix> = (0..=m).map(|j| term(&self.entries, j as i64)).collect::<Result<_, _>>()?;
203        let size = (m + 1) * n;
204        let mut big = vec![vec![Number::zero(); size]; size];
205        for bi in 0..=m {
206            for bj in bi..=m {
207                for i in 0..n {
208                    for j in 0..n {
209                        big[bi * n + i][bj * n + j] = blocks[bj - bi][i][j].clone();
210                    }
211                }
212            }
213        }
214        let e = matrix_exp(&big, &self.t)?;
215        let mut found = self.found.lock().expect("unpoisoned");
216        while found.len() <= m {
217            let k = found.len();
218            found.push((0..n).map(|i| (0..n).map(|j| e[i][k * n + j].clone()).collect()).collect());
219        }
220        Ok(found[m].clone())
221    }
222}
223
224#[derive(Debug)]
225struct MatrixExpEntry(Arc<MatrixExpSpec>, usize, usize);
226
227impl Source<Number> for MatrixExpEntry {
228    fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
229        if k < 0 {
230            return Ok(Number::zero());
231        }
232        self.0
233            .coefficient(k as usize)
234            .map(|m| m[self.1][self.2].clone())
235            .map_err(ExpansionError::Coefficient)
236    }
237}
238
239/// `e^{t·A}` for a square matrix of numbers. See the module documentation; a matrix of standard
240/// numbers that are not all rational is refused, and so is one infinite in its smallest infinitesimal.
241pub(crate) fn matrix_exp(a: &NMatrix, t: &Rational) -> Result<NMatrix, String> {
242    let n = a.len();
243    let rationals: Option<Vec<Vec<Rational>>> = a
244        .iter()
245        .map(|row| row.iter().map(|x| x.standard_rational()).collect())
246        .collect();
247    if let Some(r) = rationals {
248        let e = exp_matrix(&r, t).ok_or_else(|| "the exponential of a matrix that is not square".to_string())?;
249        return Ok(e.into_iter().map(|row| row.into_iter().map(tower::from_exact_real).collect()).collect());
250    }
251    if n == 1 {
252        let ta = Number::arith(Op::Mul, rational(t.clone()), a[0][0].clone())?;
253        return Ok(vec![vec![ta.exp().map_err(text)?]]);
254    }
255    let Some(scale) = smallest_among(a.iter().flatten()) else {
256        return Err("the exponential of a matrix of irrational standard numbers is not computed".to_string());
257    };
258    let (entries, grid) = expansions(a, &scale)?;
259    for row in &entries {
260        for e in row {
261            for k in e.floor()..0 {
262                if !e.coefficient(k).map_err(text)?.is_zero().map_err(text)? {
263                    return Err(format!(
264                        "the exponential of a matrix with an entry infinite in {} is beyond every power of it",
265                        scale.name
266                    ));
267                }
268            }
269        }
270    }
271    let spec = Arc::new(MatrixExpSpec {
272        a: a.clone(),
273        t: t.clone(),
274        scale: scale.clone(),
275        entries,
276        found: Mutex::new(Vec::new()),
277    });
278    Ok((0..n)
279        .map(|i| {
280            (0..n)
281                .map(|j| {
282                    let expansion = Expansion::source(Arc::new(MatrixExpEntry(spec.clone(), i, j)), 0, grid);
283                    Number::hyper(tower::HyperValue {
284                        value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
285                            &spec.scale,
286                            GermRecipe::MatrixExp(spec.clone(), i, j),
287                            expansion,
288                        ))),
289                        scale: spec.scale.clone(),
290                    })
291                })
292                .collect()
293        })
294        .collect())
295}
296
297// ── a flow whose rates are values of larger infinitesimals ───────────────────────────────────
298
299/// The data of a flow stepped by `scale` with rates of larger infinitesimals, shared by its state's
300/// components. See the module documentation.
301#[derive(Debug)]
302pub(crate) struct FlowSpec {
303    pub(crate) a: NMatrix,
304    pub(crate) t: Rational,
305    pub(crate) delta: Rational,
306    pub(crate) start: Vec<Number>,
307    scale: Arc<Scale>,
308    entries: Vec<Vec<Expansion<Number>>>,
309    /// The update's grid: `A = I + Σ_{k≥1} u^k·A_k`, `u = s^(1/grid)`.
310    grid: u32,
311    starts: Vec<Expansion<Number>>,
312    /// The state's grid and lowest order.
313    q: u32,
314    floor: i64,
315    exp_t: NMatrix,
316    exponent: Mutex<Vec<NMatrix>>,
317    expansion: Mutex<Vec<NMatrix>>,
318    state: Mutex<BTreeMap<i64, Vec<Number>>>,
319}
320
321impl FlowSpec {
322    fn width(&self) -> usize {
323        self.a.len()
324    }
325
326    fn term(&self, k: usize) -> Result<NMatrix, String> {
327        term(&self.entries, k as i64)
328    }
329
330    /// `C_j = T·L_{grid+j} + δ·L_j`.
331    fn exponent(&self, j: usize) -> Result<NMatrix, String> {
332        let mut cache = self.exponent.lock().expect("unpoisoned");
333        while cache.len() < j {
334            let i = cache.len() + 1;
335            let n = self.width();
336            let terms = |k: usize| self.term(k);
337            let c = mat_add(
338                &mat_scale(&log_term(&terms, n, i + self.grid as usize)?, &rational(self.t.clone()))?,
339                &mat_scale(&log_term(&terms, n, i)?, &rational(self.delta.clone()))?,
340            )?;
341            cache.push(c);
342        }
343        Ok(cache[j - 1].clone())
344    }
345
346    /// Whether the exponent's terms through `C_reach` commute, which makes their exponential the
347    /// product the recurrence reads.
348    fn commuting_through(&self, reach: usize) -> Result<bool, String> {
349        let cs: Vec<NMatrix> = (1..=reach).map(|j| self.exponent(j)).collect::<Result<_, _>>()?;
350        for (i, a) in cs.iter().enumerate() {
351            for b in &cs[i + 1..] {
352                let (ab, ba) = (mat_mul(a, b)?, mat_mul(b, a)?);
353                let difference: NMatrix = ab
354                    .iter()
355                    .zip(&ba)
356                    .map(|(x, y)| x.iter().zip(y).map(|(p, q)| p.sub(q).map_err(text)).collect())
357                    .collect::<Result<_, String>>()?;
358                if !is_zero_matrix(&difference)? {
359                    return Ok(false);
360                }
361            }
362        }
363        Ok(true)
364    }
365
366    fn expansion(&self, m: usize) -> Result<NMatrix, String> {
367        let mut cache = self.expansion.lock().expect("unpoisoned");
368        if cache.is_empty() {
369            cache.push(identity(self.width()));
370        }
371        while cache.len() <= m {
372            let mm = cache.len();
373            let mut total = zeros(self.width());
374            for k in 1..=mm {
375                let c = self.exponent(k)?;
376                if is_zero_matrix(&c)? {
377                    continue;
378                }
379                let term = mat_mul(&c, &cache[mm - k])?;
380                total = mat_add(&total, &mat_scale(&term, &rational(Rational::from_i64(k as i64)))?)?;
381            }
382            cache.push(mat_scale(&total, &rational(Rational::from_ratio_i64(1, mm as i64).expect("mm ≥ 1")))?);
383        }
384        Ok(cache[m].clone())
385    }
386
387    /// `s_m = e^{T·A_grid}·Σ_{a·r+b=m} E_a·x₀_b`, in units of `s^(1/q)`.
388    fn state(&self, m: i64) -> Result<Vec<Number>, String> {
389        if let Some(s) = self.state.lock().expect("unpoisoned").get(&m) {
390            return Ok(s.clone());
391        }
392        let r = i64::from(self.q / self.grid);
393        let reach = ((m - self.floor).max(0) / r) as usize;
394        let nonzero_exponent = (1..=reach)
395            .map(|j| self.exponent(j).and_then(|c| is_zero_matrix(&c).map(|z| !z)))
396            .collect::<Result<Vec<bool>, String>>()?;
397        if nonzero_exponent.iter().any(|b| *b) && !self.commuting_through(reach)? {
398            return Err(format!(
399                "the {} term of this flow is not computed exactly: its update matrices do not commute",
400                crate::puiseux::power_text(&self.scale.name, &Rational::from_ratio_i64(m, i64::from(self.q)).expect("q > 0"))
401            ));
402        }
403        let n = self.width();
404        let mut inner = vec![Number::zero(); n];
405        for a in 0..=reach {
406            let e = self.expansion(a)?;
407            if is_zero_matrix(&e)? {
408                continue;
409            }
410            let start: Vec<Number> = self
411                .starts
412                .iter()
413                .map(|g| g.coefficient(m - a as i64 * r).map_err(text))
414                .collect::<Result<_, _>>()?;
415            let moved = mat_vec(&e, &start)?;
416            inner = inner
417                .iter()
418                .zip(&moved)
419                .map(|(x, y)| x.add(y).map_err(text))
420                .collect::<Result<_, _>>()?;
421        }
422        let s = mat_vec(&self.exp_t, &inner)?;
423        self.state.lock().expect("unpoisoned").insert(m, s.clone());
424        Ok(s)
425    }
426}
427
428#[derive(Debug)]
429struct FlowComponent(Arc<FlowSpec>, usize);
430
431impl Source<Number> for FlowComponent {
432    fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
433        self.0.state(k).map(|s| s[self.1].clone()).map_err(ExpansionError::Coefficient)
434    }
435}
436
437/// The state after `N = T/s + δ` steps of `x ↦ A·x`, `A`'s entries values of `scale` and the larger
438/// infinitesimals: each component a germ of several infinitesimals at `scale`.
439pub(crate) fn flow(scale: &Arc<Scale>, a: &NMatrix, t: Rational, delta: Rational, start: &[Number]) -> Result<Vec<Number>, String> {
440    let n = a.len();
441    let (entries, grid) = expansions(a, scale)?;
442    let at_zero = term(&entries, 0)?;
443    let difference: NMatrix = at_zero
444        .iter()
445        .zip(identity(n))
446        .map(|(x, y)| x.iter().zip(&y).map(|(p, q)| p.sub(q).map_err(text)).collect())
447        .collect::<Result<_, String>>()?;
448    if !is_zero_matrix(&difference)? {
449        return Err(format!(
450            "the update changes its state at every step by an amount that is no infinitesimal of {}, so this supertask is no flow of it",
451            scale.name
452        ));
453    }
454    for k in 1..grid as i64 {
455        if !is_zero_matrix(&term(&entries, k)?)? {
456            return Err(format!(
457                "the update has a term of a fractional order of {} below its first, so this supertask is not a flow",
458                scale.name
459            ));
460        }
461    }
462    let starts: Vec<Expansion<Number>> = start
463        .iter()
464        .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
465        .collect::<Result<_, _>>()?;
466    let q = starts.iter().fold(grid, |l, e| lcm(l, e.ramification()));
467    let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
468    let floor = starts.iter().map(Expansion::floor).min().unwrap_or(0);
469    let exp_t = matrix_exp(&term(&entries, i64::from(grid))?, &t)?;
470    let spec = Arc::new(FlowSpec {
471        a: a.clone(),
472        t,
473        delta,
474        start: start.to_vec(),
475        scale: scale.clone(),
476        entries,
477        grid,
478        starts,
479        q,
480        floor,
481        exp_t,
482        exponent: Mutex::new(Vec::new()),
483        expansion: Mutex::new(Vec::new()),
484        state: Mutex::new(BTreeMap::new()),
485    });
486    Ok((0..n)
487        .map(|i| {
488            let expansion = Expansion::source(Arc::new(FlowComponent(spec.clone(), i)), floor, q);
489            Number::hyper(tower::HyperValue {
490                value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
491                    scale,
492                    GermRecipe::Flow(spec.clone(), i),
493                    expansion,
494                ))),
495                scale: scale.clone(),
496            })
497        })
498        .collect())
499}
500
501// ── a power whose rates are values of a smaller infinitesimal ────────────────────────────────
502
503/// The data of `A^N·x₀` for `A = I + Σ_{k≥1} w^k·A_k`, `w` a root of `scale`. See the module
504/// documentation.
505#[derive(Debug)]
506pub(crate) struct PowerSpec {
507    pub(crate) a: NMatrix,
508    pub(crate) count: Number,
509    pub(crate) start: Vec<Number>,
510    entries: Vec<Vec<Expansion<Number>>>,
511    starts: Vec<Expansion<Number>>,
512    /// `(log A)^j` truncated, by `j` and order.
513    logs: Mutex<Vec<Vec<NMatrix>>>,
514    found: Mutex<BTreeMap<i64, Vec<Number>>>,
515}
516
517impl PowerSpec {
518    fn width(&self) -> usize {
519        self.a.len()
520    }
521
522    /// `[(log A)^j]_k` for every `j ≤ k ≤ m`.
523    fn log_powers(&self, m: usize) -> Result<Vec<Vec<NMatrix>>, String> {
524        let n = self.width();
525        let mut logs = self.logs.lock().expect("unpoisoned");
526        if logs.first().is_none_or(|row| row.len() <= m) {
527            let terms = |k: usize| term(&self.entries, k as i64);
528            let l: Vec<NMatrix> = (0..=m)
529                .map(|k| if k == 0 { Ok(zeros(n)) } else { log_term(&terms, n, k) })
530                .collect::<Result<_, _>>()?;
531            let mut powers: Vec<Vec<NMatrix>> = vec![(0..=m).map(|k| if k == 0 { identity(n) } else { zeros(n) }).collect()];
532            for j in 1..=m {
533                let previous = &powers[j - 1];
534                let mut next = vec![zeros(n); m + 1];
535                for (a, p) in previous.iter().enumerate() {
536                    if is_zero_matrix(p)? {
537                        continue;
538                    }
539                    for (b, lb) in l.iter().enumerate().skip(1) {
540                        if a + b > m {
541                            break;
542                        }
543                        next[a + b] = mat_add(&next[a + b], &mat_mul(p, lb)?)?;
544                    }
545                }
546                powers.push(next);
547            }
548            *logs = powers;
549        }
550        Ok(logs.clone())
551    }
552
553    /// `P_m = Σ_{j ≤ m} (N^j/j!)·[(log A)^j]_m`.
554    fn power(&self, m: usize) -> Result<NMatrix, String> {
555        let logs = self.log_powers(m)?;
556        let n = self.width();
557        let mut total = zeros(n);
558        let mut weight = Number::one();
559        for (j, by_order) in logs.iter().enumerate().take(m + 1) {
560            if j > 0 {
561                weight = weight
562                    .mul(&self.count)
563                    .map_err(text)?
564                    .mul(&rational(Rational::from_ratio_i64(1, j as i64).expect("j ≥ 1")))
565                    .map_err(text)?;
566            }
567            total = mat_add(&total, &mat_scale(&by_order[m], &weight)?)?;
568        }
569        Ok(total)
570    }
571
572    /// The state's coefficient of order `m` on the state's grid: `Σ_{a·r+b=m} P_a·x₀_b`.
573    fn state(&self, m: i64) -> Result<Vec<Number>, String> {
574        if let Some(s) = self.found.lock().expect("unpoisoned").get(&m) {
575            return Ok(s.clone());
576        }
577        let floor = self.starts.iter().map(Expansion::floor).min().unwrap_or(0);
578        let reach = (m - floor).max(0) as usize;
579        let n = self.width();
580        let mut out = vec![Number::zero(); n];
581        for a in 0..=reach {
582            let p = self.power(a)?;
583            if is_zero_matrix(&p)? {
584                continue;
585            }
586            let start: Vec<Number> = self
587                .starts
588                .iter()
589                .map(|g| g.coefficient(m - a as i64).map_err(text))
590                .collect::<Result<_, _>>()?;
591            let moved = mat_vec(&p, &start)?;
592            out = out.iter().zip(&moved).map(|(x, y)| x.add(y).map_err(text)).collect::<Result<_, _>>()?;
593        }
594        self.found.lock().expect("unpoisoned").insert(m, out.clone());
595        Ok(out)
596    }
597}
598
599/// `A^N·x₀` for a count infinite in `scale` itself — `⌈1/(dt·dx)⌉` steps of `1 + dt·dx` — as
600/// `exp(N·log A)`: `A` the identity at order `0` in `scale`, `log A = Σ_{k≥1} L_k·u^k` its series
601/// ([`log_term`]), and `N·log A` the product of the two expansions, limited in `scale` whenever the
602/// steps the count takes add up to a limited amount. Its exponential is taken at `scale`'s orders as
603/// any matrix's ([`MatrixExpSpec`]); each component of the end state keeps the power's recipe, which
604/// names the update, the count and the start.
605fn logarithmic_power(
606    scale: &Arc<Scale>,
607    a: &NMatrix,
608    count: Number,
609    counted: Expansion<Number>,
610    start: &[Number],
611) -> Result<Vec<Number>, String> {
612    let n = a.len();
613    let (entries, grid) = expansions(a, scale)?;
614    if !is_zero_matrix(&mat_add(&term(&entries, 0)?, &mat_scale(&identity(n), &rational(Rational::from_i64(-1)))?)?)? {
615        return Err(format!(
616            "the update holds {} and changes its state at every step by more than a value of it, so its power is not computed",
617            scale.name
618        ));
619    }
620    let starts: Vec<Expansion<Number>> = start
621        .iter()
622        .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
623        .collect::<Result<_, _>>()?;
624    let q = starts.iter().fold(lcm(grid, counted.ramification()), |l, e| lcm(l, e.ramification()));
625    let entries: Vec<Vec<Expansion<Number>>> = entries
626        .into_iter()
627        .map(|row| row.into_iter().map(|e| e.refined(q / grid)).collect())
628        .collect();
629    let counted = counted.refined(q / counted.ramification());
630    let logs = Arc::new(LogSpec {
631        entries: entries.clone(),
632        width: n,
633        found: Mutex::new(Vec::new()),
634    });
635    let scaled: Vec<Vec<Expansion<Number>>> = (0..n)
636        .map(|i| {
637            (0..n)
638                .map(|j| counted.mul(&Expansion::source(Arc::new(LogEntry(logs.clone(), i, j)), 1, q)))
639                .collect()
640        })
641        .collect();
642    for row in &scaled {
643        for e in row {
644            for k in e.floor()..0 {
645                if !e.coefficient(k).map_err(text)?.is_zero().map_err(text)? {
646                    return Err(format!(
647                        "this supertask's step count {} outgrows its steps: the change they add up to is infinite in {}",
648                        count.display_string(),
649                        scale.name
650                    ));
651                }
652            }
653        }
654    }
655    let exponential = Arc::new(MatrixExpSpec {
656        a: a.clone(),
657        t: Rational::one(),
658        scale: scale.clone(),
659        entries: scaled,
660        found: Mutex::new(Vec::new()),
661    });
662    let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
663    let spec = Arc::new(PowerSpec {
664        a: a.clone(),
665        count,
666        start: start.to_vec(),
667        entries,
668        starts: starts.clone(),
669        logs: Mutex::new(Vec::new()),
670        found: Mutex::new(BTreeMap::new()),
671    });
672    Ok((0..n)
673        .map(|i| {
674            let expansion = (0..n).fold(Expansion::constant(Number::zero()), |sum, j| {
675                let entry = Expansion::source(Arc::new(MatrixExpEntry(exponential.clone(), i, j)), 0, q);
676                sum.add(&entry.mul(&starts[j]))
677            });
678            Number::hyper(tower::HyperValue {
679                value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
680                    scale,
681                    GermRecipe::Power(spec.clone(), i),
682                    expansion,
683                ))),
684                scale: scale.clone(),
685            })
686        })
687        .collect())
688}
689
690/// The series of `log A` for `A` the identity at order `0`, shared by its entries' expansions.
691#[derive(Debug)]
692struct LogSpec {
693    entries: Vec<Vec<Expansion<Number>>>,
694    width: usize,
695    /// `L_k` for each order `k` found so far, `L_0 = 0`.
696    found: Mutex<Vec<NMatrix>>,
697}
698
699impl LogSpec {
700    fn order(&self, k: usize) -> Result<NMatrix, String> {
701        let mut found = self.found.lock().expect("unpoisoned");
702        while found.len() <= k {
703            let at = found.len();
704            let next = if at == 0 {
705                zeros(self.width)
706            } else {
707                log_term(&|step| term(&self.entries, step as i64), self.width, at)?
708            };
709            found.push(next);
710        }
711        Ok(found[k].clone())
712    }
713}
714
715#[derive(Debug)]
716struct LogEntry(Arc<LogSpec>, usize, usize);
717
718impl Source<Number> for LogEntry {
719    fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
720        if k < 1 {
721            return Ok(Number::zero());
722        }
723        self.0.order(k as usize).map(|m| m[self.1][self.2].clone()).map_err(ExpansionError::Coefficient)
724    }
725}
726
727#[derive(Debug)]
728struct PowerComponent(Arc<PowerSpec>, usize);
729
730impl Source<Number> for PowerComponent {
731    fn coefficient(&self, k: i64) -> Result<Number, ExpansionError> {
732        self.0.state(k).map(|s| s[self.1].clone()).map_err(ExpansionError::Coefficient)
733    }
734}
735
736/// `A^N·x₀` for `A`'s entries values of the infinitesimal `scale` (smaller than the one `N` counts)
737/// and larger ones: exactly, by `exp(N·log A)` in `scale`, when `A` is the identity at order `0`; a
738/// scalar update otherwise by `exp(N·ln A)`; a matrix update otherwise refused by name.
739pub(crate) fn power(scale: &Arc<Scale>, a: &NMatrix, count: Number, start: &[Number]) -> Result<Vec<Number>, String> {
740    let counted = tower::number_germ(&count, scale)?.expansion.clone();
741    if counted.floor() < 0 {
742        return logarithmic_power(scale, a, count, counted, start);
743    }
744    let n = a.len();
745    let (entries, grid) = expansions(a, scale)?;
746    let at_zero = term(&entries, 0)?;
747    let difference: NMatrix = at_zero
748        .iter()
749        .zip(identity(n))
750        .map(|(x, y)| x.iter().zip(&y).map(|(p, q)| p.sub(q).map_err(text)).collect())
751        .collect::<Result<_, String>>()?;
752    if !is_zero_matrix(&difference)? {
753        if n == 1 {
754            let g = tower::number_germ(&a[0][0], scale)?;
755            let logarithm = Arc::new(NestedGerm::ln(&g, &a[0][0].display_string())?);
756            let times = Arc::new(NestedGerm::mul(&logarithm, &tower::number_germ(&count, scale)?));
757            let grown = Arc::new(NestedGerm::exp(&times, &times.render())?);
758            let end = NestedGerm::mul(&grown, &tower::number_germ(&start[0], scale)?);
759            return Ok(vec![Number::hyper(tower::HyperValue {
760                value: tower::HyperForm::Nested(Arc::new(end)),
761                scale: scale.clone(),
762            })]);
763        }
764        return Err(format!(
765            "the update holds {} and changes its state at every step by more than a value of it, so its power is not computed",
766            scale.name
767        ));
768    }
769    let starts: Vec<Expansion<Number>> = start
770        .iter()
771        .map(|x| tower::number_germ(x, scale).map(|g| g.expansion.clone()))
772        .collect::<Result<_, _>>()?;
773    let q = starts.iter().fold(grid, |l, e| lcm(l, e.ramification()));
774    let entries: Vec<Vec<Expansion<Number>>> = entries
775        .into_iter()
776        .map(|row| row.into_iter().map(|e| e.refined(q / grid)).collect())
777        .collect();
778    let starts: Vec<Expansion<Number>> = starts.into_iter().map(|e| e.refined(q / e.ramification())).collect();
779    let floor = starts.iter().map(Expansion::floor).min().unwrap_or(0);
780    let spec = Arc::new(PowerSpec {
781        a: a.clone(),
782        count,
783        start: start.to_vec(),
784        entries,
785        starts,
786        logs: Mutex::new(Vec::new()),
787        found: Mutex::new(BTreeMap::new()),
788    });
789    Ok((0..n)
790        .map(|i| {
791            let expansion = Expansion::source(Arc::new(PowerComponent(spec.clone(), i)), floor, q);
792            Number::hyper(tower::HyperValue {
793                value: tower::HyperForm::Nested(Arc::new(NestedGerm::from_source(
794                    scale,
795                    GermRecipe::Power(spec.clone(), i),
796                    expansion,
797                ))),
798                scale: scale.clone(),
799            })
800        })
801        .collect())
802}