use crate::error::OptimizeError;
use scirs2_core::ndarray::{Array2, ArrayView2};
use scirs2_sparse::csr_array::CsrArray;
use scirs2_sparse::SparseArray;
use std::collections::HashSet;
pub type CompressedJacobianPattern = (CsrArray<f64>, Array2<f64>, Array2<f64>);
#[allow(dead_code)]
pub fn compress_jacobian_pattern(
sparsity: &CsrArray<f64>,
) -> Result<CompressedJacobianPattern, OptimizeError> {
let (m, n) = sparsity.shape();
let coloring = color_jacobian_columns(sparsity)?;
let num_colors = coloring.iter().max().unwrap_or(&0) + 1;
let mut b = Array2::zeros((n, num_colors));
for (col, &color) in coloring.iter().enumerate() {
b[[col, color]] = 1.0;
}
let c = Array2::eye(m);
Ok((sparsity.clone(), b, c))
}
#[allow(dead_code)]
fn color_jacobian_columns(sparsity: &CsrArray<f64>) -> Result<Vec<usize>, OptimizeError> {
let (_m, n) = sparsity.shape();
let mut coloring = vec![0; n];
for col in 0..n {
let mut used_colors = HashSet::new();
let col_rows = get_column_nonzero_rows(sparsity, col);
for prev_col in 0..col {
let prev_col_rows = get_column_nonzero_rows(sparsity, prev_col);
if col_rows.iter().any(|&row| prev_col_rows.contains(&row)) {
used_colors.insert(coloring[prev_col]);
}
}
let mut color = 0;
while used_colors.contains(&color) {
color += 1;
}
coloring[col] = color;
}
Ok(coloring)
}
#[allow(dead_code)]
fn get_column_nonzero_rows(sparsity: &CsrArray<f64>, col: usize) -> HashSet<usize> {
let mut rows = HashSet::new();
let (m, _) = sparsity.shape();
for row in 0..m {
let val = sparsity.get(row, col);
if val.abs() > 1e-15 {
rows.insert(row);
}
}
rows
}
#[allow(dead_code)]
pub fn compress_hessian_pattern(
sparsity: &CsrArray<f64>,
) -> Result<(CsrArray<f64>, Array2<f64>), OptimizeError> {
let (n, _) = sparsity.shape();
let coloring = color_hessian_columns(sparsity)?;
let num_colors = coloring.iter().max().unwrap_or(&0) + 1;
let mut p = Array2::zeros((n, num_colors));
for (col, &color) in coloring.iter().enumerate() {
p[[col, color]] = 1.0;
}
Ok((sparsity.clone(), p))
}
#[allow(dead_code)]
fn color_hessian_columns(sparsity: &CsrArray<f64>) -> Result<Vec<usize>, OptimizeError> {
let (n, _) = sparsity.shape();
let mut coloring = vec![0; n];
let adjacency = build_adjacency_list(sparsity);
for col in 0..n {
let mut used_colors = HashSet::new();
let neighbors = &adjacency[col];
for prev_col in 0..col {
let prev_neighbors = &adjacency[prev_col];
let distance_conflict =
neighbors.contains(&prev_col) ||
neighbors.iter().any(|&n| prev_neighbors.contains(&n));
if distance_conflict {
used_colors.insert(coloring[prev_col]);
}
}
let mut color = 0;
while used_colors.contains(&color) {
color += 1;
}
coloring[col] = color;
}
Ok(coloring)
}
#[allow(dead_code)]
fn build_adjacency_list(sparsity: &CsrArray<f64>) -> Vec<HashSet<usize>> {
let (n, _) = sparsity.shape();
let mut adjacency = vec![HashSet::new(); n];
for i in 0..n {
for j in 0..n {
if i != j {
let val = sparsity.get(i, j);
if val.abs() > 1e-15 {
adjacency[i].insert(j);
adjacency[j].insert(i); }
}
}
}
adjacency
}
#[allow(dead_code)]
pub fn reconstruct_jacobian(
gradients: &ArrayView2<f64>,
b: &ArrayView2<f64>,
_c: &ArrayView2<f64>,
sparsity: &CsrArray<f64>,
) -> Result<CsrArray<f64>, OptimizeError> {
let (m, n) = sparsity.shape();
let (grad_m, num_colors) = gradients.dim();
let (b_n, b_colors) = b.dim();
if grad_m != m || b_n != n || b_colors != num_colors {
return Err(OptimizeError::InvalidInput(
"Dimension mismatch in reconstruction matrices".to_string(),
));
}
let mut jacobian_dense = Array2::zeros((m, n));
for color in 0..num_colors {
let mut columns_in_color = Vec::new();
for col in 0..n {
if b[[col, color]].abs() > 1e-15 {
columns_in_color.push(col);
}
}
for &col in &columns_in_color {
for row in 0..m {
let val = sparsity.get(row, col);
if val.abs() > 1e-15 {
jacobian_dense[[row, col]] = gradients[[row, color]];
}
}
}
}
let mut row_indices = Vec::new();
let mut col_indices = Vec::new();
let mut values = Vec::new();
for row in 0..m {
for col in 0..n {
let sparsity_val = sparsity.get(row, col);
if sparsity_val.abs() > 1e-15 && jacobian_dense[[row, col]].abs() > 1e-15 {
row_indices.push(row);
col_indices.push(col);
values.push(jacobian_dense[[row, col]]);
}
}
}
CsrArray::from_triplets(&row_indices, &col_indices, &values, (m, n), false)
.map_err(|_| OptimizeError::InvalidInput("Failed to create sparse matrix".to_string()))
}
#[allow(dead_code)]
pub fn reconstruct_hessian_central_diff(
gradients_forward: &ArrayView2<f64>,
gradients_backward: &ArrayView2<f64>,
p: &ArrayView2<f64>,
sparsity: &CsrArray<f64>,
h: f64,
) -> Result<CsrArray<f64>, OptimizeError> {
let (n, _) = sparsity.shape();
let (grad_n, num_colors) = gradients_forward.dim();
let (p_n, p_colors) = p.dim();
if grad_n != n || p_n != n || p_colors != num_colors {
return Err(OptimizeError::InvalidInput(
"Dimension mismatch in Hessian reconstruction matrices".to_string(),
));
}
if gradients_backward.dim() != (n, num_colors) {
return Err(OptimizeError::InvalidInput(
"Gradient matrices must have the same dimensions".to_string(),
));
}
let mut hessian_dense = Array2::zeros((n, n));
for color in 0..num_colors {
let mut columns_in_color = Vec::new();
for col in 0..n {
if p[[col, color]].abs() > 1e-15 {
columns_in_color.push(col);
}
}
for &col_i in &columns_in_color {
for row in 0..n {
if sparsity.get(row, col_i).abs() > 1e-15 {
let h_val = (gradients_forward[[row, color]]
- gradients_backward[[row, color]])
/ (2.0 * h);
hessian_dense[[row, col_i]] = h_val;
if row != col_i && sparsity.get(col_i, row).abs() > 1e-15 {
hessian_dense[[col_i, row]] = h_val;
}
}
}
}
}
let mut row_indices = Vec::new();
let mut col_indices = Vec::new();
let mut values = Vec::new();
for row in 0..n {
for col in 0..n {
if sparsity.get(row, col).abs() > 1e-15 && hessian_dense[[row, col]].abs() > 1e-15 {
row_indices.push(row);
col_indices.push(col);
values.push(hessian_dense[[row, col]]);
}
}
}
CsrArray::from_triplets(&row_indices, &col_indices, &values, (n, n), false)
.map_err(|_| OptimizeError::ValueError("Failed to create sparse Hessian".to_string()))
}
#[allow(dead_code)]
pub fn reconstruct_hessian(
gradients: &ArrayView2<f64>,
p: &ArrayView2<f64>,
sparsity: &CsrArray<f64>,
) -> Result<CsrArray<f64>, OptimizeError> {
let h = 1e-8; let gradients_backward = Array2::zeros(gradients.dim());
let gradients_backward_view = gradients_backward.view();
reconstruct_hessian_central_diff(gradients, &gradients_backward_view, p, sparsity, h)
}