Skip to main content

tensor4all_core/
floating_zone.rs

1//! Greedy coordinate-descent (floating-zone) search for high-error points.
2//!
3//! The floating-zone walk is the strongest global pivot search used in this
4//! codebase: from a starting point it repeatedly sweeps every site
5//! coordinate, moving each coordinate to the value with the largest
6//! interpolation error, until the error stops improving or exceeds a
7//! tolerance. It is a strict generalization of the single-cross search used
8//! elsewhere (one sweep with no repeats equals a cross scan).
9
10use crate::MultiIndex;
11
12/// Walk one floating-zone search trajectory.
13///
14/// Mirrors `TensorCrossInterpolation.jl`'s `_floatingzone`: starting from
15/// `init_p`, each sweep visits every site in order and moves that site's
16/// coordinate to the value with the largest error (as measured by
17/// `eval_batch`), keeping the running maximum error monotonically
18/// non-decreasing. The walk stops when a sweep does not increase the
19/// maximum error (the trajectory is stuck on a local maximum) or when the
20/// maximum error exceeds `early_stop_tol` (the point is already
21/// significant), or after `max_sweeps` sweeps as a safety bound.
22///
23/// # Arguments
24///
25/// * `local_dims` - Local dimension of each site.
26/// * `init_p` - Starting multi-index; must have length `local_dims.len()`.
27/// * `max_sweeps` - Upper bound on the number of coordinate sweeps. The
28///   no-improvement early stop almost always fires first.
29/// * `early_stop_tol` - Stop walking once the maximum error exceeds this
30///   value; the caller has found a significantly wrong point.
31/// * `eval_batch` - Evaluates the error magnitude `|f - tt|` at a batch of
32///   multi-indices. Called once per site per sweep with that site's
33///   `local_dims[site]` candidate points (the current pivot with the site
34///   coordinate varied), so callers can batch shared contractions.
35///
36/// # Returns
37///
38/// The final pivot and the maximum error encountered along the walk. The
39/// returned error may exceed `early_stop_tol`; the caller decides whether
40/// the point is significant.
41///
42/// # Errors
43///
44/// Propagates the error returned by `eval_batch` unchanged — typically an
45/// operation failure or an index mismatch from the underlying evaluator.
46pub fn floating_zone_walk<E, Err>(
47    local_dims: &[usize],
48    init_p: &MultiIndex,
49    max_sweeps: usize,
50    early_stop_tol: f64,
51    mut eval_batch: E,
52) -> std::result::Result<(MultiIndex, f64), Err>
53where
54    E: FnMut(&[MultiIndex]) -> std::result::Result<Vec<f64>, Err>,
55{
56    let n = local_dims.len();
57
58    let mut pivot = init_p.clone();
59
60    // Initial error at the starting point.
61    let start_errors = eval_batch(&[pivot.clone()])?;
62    let mut max_error = start_errors.first().copied().unwrap_or(0.0);
63
64    for _ in 0..max_sweeps {
65        let prev_max_error = max_error;
66        for ipos in 0..n {
67            // Candidate points: every value at this site, the rest fixed at
68            // the current pivot (updated greedily within this sweep).
69            let mut points = Vec::with_capacity(local_dims[ipos]);
70            for value in 0..local_dims[ipos] {
71                let mut point = pivot.clone();
72                point[ipos] = value;
73                points.push(point);
74            }
75            let errors = eval_batch(&points)?;
76
77            let mut best_local_idx = pivot[ipos];
78            let mut best_local_error = 0.0f64;
79            for (value, &error) in errors.iter().enumerate() {
80                if error > best_local_error {
81                    best_local_error = error;
82                    best_local_idx = value;
83                }
84            }
85            pivot[ipos] = best_local_idx;
86            max_error = max_error.max(best_local_error);
87        }
88
89        if max_error == prev_max_error || max_error > early_stop_tol {
90            break;
91        }
92    }
93
94    Ok((pivot, max_error))
95}