///|
pub(all) struct BatchSummary {
mean_spectrum : Spectrum
sample_deviations : Array[Double]
count : Int32
}
///|
/// Stable per-channel Welford statistics on an exactly common axis.
pub fn summarize_batch(
batch : Array[Spectrum],
) -> BatchSummary raise SpectralError {
if batch.length() < 2 || batch.length() > 256 {
raise Budget("summary needs 2..256 spectra")
}
let n = batch[0].length()
if n * batch.length() > 1048576 {
raise Budget("summary exceeds sample budget")
}
for input in batch {
require_same_axis(batch[0], input, 0.0)
}
let average = Array::make(n, 0.0)
let m2 = Array::make(n, 0.0)
for k in 0.. String {
match error {
Invalid(message) => "invalid: " + message
Numerical(message) => "numerical: " + message
Budget(message) => "budget: " + message
}
}
///|
/// Transform independently against one frozen model; keep every input index.
/// A bad sample is reported, never silently removed or used to refit the model.
pub fn scatter_batch(
model : ScatterModel,
inputs : Array[Spectrum],
) -> Array[ScatterOutcome] raise SpectralError {
if inputs.length() < 1 || inputs.length() > 256 {
raise Budget("batch needs 1..256 spectra")
}
let mut samples = 0
for input in inputs {
samples = samples + input.length()
}
if samples > 1048576 {
raise Budget("batch exceeds sample budget")
}
let outcomes : Array[ScatterOutcome] = []
for i in 0.. Rejected(i, error_message(error))
}
outcomes.push(outcome)
}
outcomes
}
///|
/// Resample multiple measured spectra onto one owned common axis.
/// Any out-of-domain input fails the operation; no partial batch escapes.
pub fn align_batch(
inputs : Array[Spectrum],
axis : Array[Double],
) -> Array[Spectrum] raise SpectralError {
if inputs.length() < 1 || inputs.length() > 256 {
raise Budget("alignment needs 1..256 spectra")
}
if axis.length() > 65536 || axis.length() * inputs.length() > 1048576 {
raise Budget("aligned batch exceeds sample budget")
}
let output : Array[Spectrum] = []
for input in inputs {
output.push(resample(input, axis))
}
output
}