pub mod ensemble;
pub mod kriging;
pub mod rbf_surrogate;
pub use ensemble::{EnsembleOptions, EnsembleSurrogate, ModelSelectionCriterion};
pub use kriging::{CorrelationFunction, KrigingOptions, KrigingSurrogate};
pub use rbf_surrogate::{RbfKernel, RbfOptions, RbfSurrogate};
use crate::error::{OptimizeError, OptimizeResult};
use scirs2_core::ndarray::{Array1, Array2};
pub trait SurrogateModel {
fn fit(&mut self, x: &Array2<f64>, y: &Array1<f64>) -> OptimizeResult<()>;
fn predict(&self, x: &Array1<f64>) -> OptimizeResult<f64>;
fn predict_with_uncertainty(&self, x: &Array1<f64>) -> OptimizeResult<(f64, f64)>;
fn predict_batch(&self, x: &Array2<f64>) -> OptimizeResult<Array1<f64>> {
let n = x.nrows();
let mut predictions = Array1::zeros(n);
for i in 0..n {
predictions[i] = self.predict(&x.row(i).to_owned())?;
}
Ok(predictions)
}
fn n_samples(&self) -> usize;
fn n_features(&self) -> usize;
fn update(&mut self, x: &Array1<f64>, y: f64) -> OptimizeResult<()>;
}
pub fn pairwise_sq_distances(x: &Array2<f64>, y: &Array2<f64>) -> Array2<f64> {
let n = x.nrows();
let m = y.nrows();
let mut dists = Array2::zeros((n, m));
for i in 0..n {
for j in 0..m {
let mut sq_dist = 0.0;
for k in 0..x.ncols() {
let diff = x[[i, k]] - y[[j, k]];
sq_dist += diff * diff;
}
dists[[i, j]] = sq_dist;
}
}
dists
}
pub fn solve_spd(a: &Array2<f64>, b: &Array1<f64>) -> OptimizeResult<Array1<f64>> {
let n = a.nrows();
if n != a.ncols() {
return Err(OptimizeError::InvalidInput(
"Matrix must be square".to_string(),
));
}
if n != b.len() {
return Err(OptimizeError::InvalidInput(
"Matrix and vector dimensions must match".to_string(),
));
}
let mut l = Array2::zeros((n, n));
for j in 0..n {
let mut sum = 0.0;
for k in 0..j {
sum += l[[j, k]] * l[[j, k]];
}
let diag = a[[j, j]] - sum;
if diag <= 0.0 {
return Err(OptimizeError::ComputationError(
"Matrix is not positive definite".to_string(),
));
}
l[[j, j]] = diag.sqrt();
for i in (j + 1)..n {
let mut sum = 0.0;
for k in 0..j {
sum += l[[i, k]] * l[[j, k]];
}
l[[i, j]] = (a[[i, j]] - sum) / l[[j, j]];
}
}
let mut z = Array1::zeros(n);
for i in 0..n {
let mut sum = 0.0;
for j in 0..i {
sum += l[[i, j]] * z[j];
}
z[i] = (b[i] - sum) / l[[i, i]];
}
let mut x = Array1::zeros(n);
for i in (0..n).rev() {
let mut sum = 0.0;
for j in (i + 1)..n {
sum += l[[j, i]] * x[j];
}
x[i] = (z[i] - sum) / l[[i, i]];
}
Ok(x)
}
pub fn solve_general(a: &Array2<f64>, b: &Array1<f64>) -> OptimizeResult<Array1<f64>> {
let n = a.nrows();
if n != a.ncols() || n != b.len() {
return Err(OptimizeError::InvalidInput(
"Dimension mismatch in linear system".to_string(),
));
}
let mut lu = a.clone();
let mut perm: Vec<usize> = (0..n).collect();
for k in 0..n {
let mut max_val = lu[[k, k]].abs();
let mut max_row = k;
for i in (k + 1)..n {
if lu[[i, k]].abs() > max_val {
max_val = lu[[i, k]].abs();
max_row = i;
}
}
if max_val < 1e-30 {
return Err(OptimizeError::ComputationError(
"Singular or near-singular matrix in linear solve".to_string(),
));
}
if max_row != k {
perm.swap(k, max_row);
for j in 0..n {
let tmp = lu[[k, j]];
lu[[k, j]] = lu[[max_row, j]];
lu[[max_row, j]] = tmp;
}
}
for i in (k + 1)..n {
lu[[i, k]] /= lu[[k, k]];
for j in (k + 1)..n {
lu[[i, j]] -= lu[[i, k]] * lu[[k, j]];
}
}
}
let mut pb = Array1::zeros(n);
for i in 0..n {
pb[i] = b[perm[i]];
}
let mut y = pb;
for i in 1..n {
for j in 0..i {
y[i] -= lu[[i, j]] * y[j];
}
}
let mut x = y;
for i in (0..n).rev() {
for j in (i + 1)..n {
x[i] -= lu[[i, j]] * x[j];
}
x[i] /= lu[[i, i]];
}
Ok(x)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pairwise_distances() {
let x = Array2::from_shape_vec((2, 2), vec![0.0, 0.0, 1.0, 1.0])
.expect("Array creation failed");
let y = Array2::from_shape_vec((2, 2), vec![1.0, 0.0, 0.0, 1.0])
.expect("Array creation failed");
let dists = pairwise_sq_distances(&x, &y);
assert!((dists[[0, 0]] - 1.0).abs() < 1e-10);
assert!((dists[[0, 1]] - 1.0).abs() < 1e-10);
assert!((dists[[1, 0]] - 1.0).abs() < 1e-10);
assert!((dists[[1, 1]] - 1.0).abs() < 1e-10);
}
#[test]
fn test_solve_spd() {
let a = Array2::from_shape_vec((2, 2), vec![4.0, 2.0, 2.0, 3.0])
.expect("Array creation failed");
let b = Array1::from_vec(vec![1.0, 2.0]);
let x = solve_spd(&a, &b).expect("SPD solve failed");
assert!((x[0] - (-0.125)).abs() < 1e-10);
assert!((x[1] - 0.75).abs() < 1e-10);
}
#[test]
fn test_solve_general() {
let a = Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0])
.expect("Array creation failed");
let b = Array1::from_vec(vec![5.0, 11.0]);
let x = solve_general(&a, &b).expect("General solve failed");
assert!((x[0] - 1.0).abs() < 1e-10);
assert!((x[1] - 2.0).abs() < 1e-10);
}
}