Skip to main content

solve_bracketed_batch

Function solve_bracketed_batch 

Source
pub fn solve_bracketed_batch<F>(
    problems: &[RootProblem],
    method: RootMethod,
    settings: RootSettings,
    backend: ComputeBackend,
    f: F,
) -> RootBatch
where F: Fn(usize, f64) -> f64 + Sync,
Expand description

Solve N independent bracketed root problems without a derivative, on the chosen backend.

This is the entry point for the common case: a residual you can evaluate but whose derivative you would rather not write, bracketed per lane. Both RootMethods are globally convergent on a continuous, genuinely bracketed residual.

§Arguments

  • problems — one RootProblem per lane. problems[i]’s bracket is used with f(i, ·). The guess field is ignored.
  • method — RootMethod::Brent unless you specifically want bisection’s fixed, function-independent cost.
  • settings — tolerances and iteration cap; see RootSettings.
  • backend — requested execution backend. What actually runs is root_batch_backend_for applied to it: an unavailable backend degrades, Gpu degrades (no GPU kernel here yet), and a batch below ROOT_BATCH_MIN_PROBLEMS runs serially. None of those changes the answer.
  • f — the residual. f(i, x) must return the value of lane i’s function at abscissa x. It must be a pure deterministic function of its arguments — see the module-level “Determinism” section for what breaks if it is not. It is called from multiple threads on the CpuMulti path, hence the Sync bound; the bound is present in both feature builds so that enabling parallel never changes a public signature.

§Returns

A RootBatch with one RootSolution per problem, in problem order. An empty problems slice returns an empty batch and calls f zero times.

§Determinism

Bit-for-bit identical on ComputeBackend::Serial and ComputeBackend::CpuMulti, at any thread count, for a pure f. Both backends run the same per-lane kernel and no arithmetic crosses lanes.

§Non-convergence

Per lane, never swallowed. A lane whose bracket does not straddle a root reports RootStatus::NotBracketed and a NaN iterate; a lane that exhausts max_iterations reports RootStatus::MaxIterations and its best iterate, which RootSolution::root still refuses to hand out as a root. RootBatch::roots turns any failure into an Err.

§Verification

Methodology. The batch is checked against closed-form analytic roots, which exist for the test residuals and so are an exact oracle rather than another implementation. (x - 1)(x - 2)(x - 3) is solved on brackets isolating each of its three known roots, and x^2 - k against sqrt(k); the pass criterion is |x_computed - x_analytic| <= 1e-12 and status == Converged on every lane.

Results, measured 2026-08-12 by bracketed_matches_analytic_cubic_roots and brent_matches_analytic_sqrt in parallel/tests.rs, release build: worst absolute error over the three cubic roots 0.000000e0 for both RootMethod::Brent and RootMethod::Bisection — every lane recovered 1.0, 2.0 and 3.0 exactly — and worst absolute error over the 64 sqrt(k) lanes 1.776357e-15. All lanes converged. Interpretation: the integer cubic roots are exactly representable and both methods land on them bit for bit; the sqrt(k) roots are irrational, and 1.78e-15 is 8 units in the last place at k = 64, i.e. the rounding floor. The iteration contributes no error of its own beyond the residual’s conditioning.

§Example

use outram_foam_basic_lib::compute::ComputeBackend;
use outram_foam_basic_lib::math::parallel::{
    solve_bracketed_batch, RootMethod, RootProblem, RootSettings,
};

// Three lanes, each isolating one root of (x-1)(x-2)(x-3).
let cubic = |x: f64| (x - 1.0) * (x - 2.0) * (x - 3.0);
let problems = [
    RootProblem::new(0.5, 1.5),
    RootProblem::new(1.5, 2.5),
    RootProblem::new(2.5, 3.5),
];

let batch = solve_bracketed_batch(
    &problems,
    RootMethod::Brent,
    RootSettings::default(),
    ComputeBackend::CpuMulti,
    |_, x| cubic(x),
);

let roots = batch.roots().expect("all lanes converge");
for (got, want) in roots.iter().zip([1.0, 2.0, 3.0]) {
    assert!((got - want).abs() < 1e-12, "got {got}, want {want}");
}