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}