use crate::core::{ConstrainedResidualProblem, Error, Matrix, TikhonovRegularization};
pub fn compute_p_dense(matrix: &Matrix) -> Vec<f64> {
let rows = matrix.nrows();
let cols = matrix.ncols();
let data = matrix.data();
let mut p = vec![0.0; cols * cols];
for i in 0..cols {
for j in 0..cols {
let mut sum = 0.0;
for k in 0..rows {
sum += data[k * cols + i] * data[k * cols + j];
}
p[i * cols + j] = sum;
}
}
p
}
pub fn compute_q(matrix: &Matrix, target: &[f64]) -> Vec<f64> {
let rows = matrix.nrows();
let cols = matrix.ncols();
let data = matrix.data();
let mut q = vec![0.0; cols];
for j in 0..cols {
let mut sum = 0.0;
for i in 0..rows {
sum += data[i * cols + j] * target[i];
}
q[j] = -sum;
}
q
}
pub fn compute_residual(matrix: &Matrix, x: &[f64], target: &[f64]) -> Vec<f64> {
let rows = matrix.nrows();
let cols = matrix.ncols();
let data = matrix.data();
let mut residual = vec![0.0; rows];
for i in 0..rows {
let mut value = 0.0;
for j in 0..cols {
value += data[i * cols + j] * x[j];
}
residual[i] = value - target[i];
}
residual
}
pub fn l2_squared_value(residual: &[f64]) -> f64 {
0.5 * residual.iter().map(|value| value * value).sum::<f64>()
}
pub fn tikhonov_value(regularization: &TikhonovRegularization, x: &[f64]) -> f64 {
let residual = compute_residual(regularization.matrix(), x, regularization.target());
regularization.lambda() * l2_squared_value(&residual)
}
pub fn apply_matrix(matrix: &Matrix, x: &[f64]) -> Vec<f64> {
let rows = matrix.nrows();
let cols = matrix.ncols();
let data = matrix.data();
let mut out = vec![0.0; rows];
for i in 0..rows {
let mut value = 0.0;
for j in 0..cols {
value += data[i * cols + j] * x[j];
}
out[i] = value;
}
out
}
pub fn max_constraint_violation(problem: &ConstrainedResidualProblem, x: &[f64]) -> f64 {
let mut max_violation: f64 = 0.0;
for eq in problem.equalities() {
let vals = apply_matrix(eq.matrix(), x);
for (lhs, rhs) in vals.iter().zip(eq.rhs().iter()) {
max_violation = max_violation.max((lhs - rhs).abs());
}
}
for ineq in problem.inequalities() {
let vals = apply_matrix(ineq.matrix(), x);
for (lhs, rhs) in vals.iter().zip(ineq.rhs().iter()) {
max_violation = max_violation.max((lhs - rhs).max(0.0));
}
}
if let Some(bounds) = problem.bounds() {
for (i, x_i) in x.iter().copied().enumerate().take(bounds.len()) {
if let Some(lb) = bounds.lower()[i] {
max_violation = max_violation.max((lb - x_i).max(0.0));
}
if let Some(ub) = bounds.upper()[i] {
max_violation = max_violation.max((x_i - ub).max(0.0));
}
}
}
max_violation
}
pub fn copy_block_rows(
block_matrix: &Matrix,
a_dense: &mut [f64],
total_ncols: usize,
start_row: usize,
) -> Result<(), Error> {
if block_matrix.ncols() != total_ncols {
return Err(Error::DimensionMismatch {
message: format!(
"constraint block has {} columns but expected {}",
block_matrix.ncols(),
total_ncols
),
});
}
let rows = block_matrix.nrows();
let cols = block_matrix.ncols();
let src = block_matrix.data();
for i in 0..rows {
for j in 0..cols {
a_dense[(start_row + i) * total_ncols + j] = src[i * cols + j];
}
}
Ok(())
}