use crate::families::custom_family::family_trait::ExactNewtonJointGradientEvaluation;
use gam_problem::{CustomFamilyError, DenseMatrixHyperOperator, HyperOperator};
use ndarray::{Array1, Array2};
use std::sync::Arc;
pub use gam_problem::{
CustomFamilyBlockPsiDerivative, CustomFamilyPsiDerivativeOperator, JointHessianSourcePreference,
MaterializablePsiDerivativeOperator, MaterializationIntent, SharedDerivativeBlocks,
};
pub use gam_problem::{
ExactNewtonJointPsiSecondOrderContracted, ExactNewtonJointPsiSecondOrderTerms,
ExactNewtonJointPsiTerms,
};
pub trait ExactNewtonJointHessianWorkspace: Send + Sync {
fn warm_up_outer_caches(&self) -> Result<(), String> {
Ok(())
}
fn hessian_dense(&self) -> Result<Option<Array2<f64>>, String> {
Ok(None)
}
fn hessian_source_preference(&self) -> JointHessianSourcePreference {
JointHessianSourcePreference::Dense
}
fn hessian_source_preference_for_intent(
&self,
intent: MaterializationIntent,
) -> JointHessianSourcePreference {
match intent {
MaterializationIntent::InnerSolve
| MaterializationIntent::LogdetFactorization
| MaterializationIntent::OuterEvaluation
| MaterializationIntent::OuterGradient => self.hessian_source_preference(),
}
}
fn hessian_dense_forced(&self) -> Result<Option<Array2<f64>>, String> {
self.hessian_dense()
}
fn joint_log_likelihood_evaluation(&self) -> Result<Option<f64>, String> {
Ok(None)
}
fn joint_gradient_evaluation(
&self,
) -> Result<Option<ExactNewtonJointGradientEvaluation>, String> {
Ok(None)
}
fn hessian_matvec_available(&self) -> bool {
false
}
fn hessian_matvec(&self, arr: &Array1<f64>) -> Result<Option<Array1<f64>>, String> {
assert!(arr.iter().all(|v| !v.is_nan()));
Ok(None)
}
fn hessian_matvec_into(&self, v: &Array1<f64>, out: &mut Array1<f64>) -> Result<bool, String> {
match self.hessian_matvec(v)? {
Some(result) => {
if result.len() != out.len() {
return Err(CustomFamilyError::DimensionMismatch {
reason: format!(
"hessian_matvec_into: result length {} != out length {}",
result.len(),
out.len()
),
}
.into());
}
out.assign(&result);
Ok(true)
}
None => Ok(false),
}
}
fn hessian_apply_mat(
&self,
v_cols: &Array2<f64>,
out: &mut Array2<f64>,
) -> Result<bool, String> {
if v_cols.nrows() != out.nrows() || v_cols.ncols() != out.ncols() {
return Err(CustomFamilyError::DimensionMismatch {
reason: format!(
"hessian_apply_mat: v_cols {}x{} != out {}x{}",
v_cols.nrows(),
v_cols.ncols(),
out.nrows(),
out.ncols()
),
}
.into());
}
let total = v_cols.nrows();
let mut col_in = Array1::<f64>::zeros(total);
let mut col_out = Array1::<f64>::zeros(total);
for col in 0..v_cols.ncols() {
col_in.assign(&v_cols.column(col));
if !self.hessian_matvec_into(&col_in, &mut col_out)? {
return Ok(false);
}
out.column_mut(col).assign(&col_out);
}
Ok(true)
}
fn hessian_diagonal(&self) -> Result<Option<Array1<f64>>, String> {
Ok(None)
}
fn projected_directional_derivative_traces(
&self,
factor: &Array2<f64>,
directions: &Array2<f64>,
) -> Result<Option<Array1<f64>>, String> {
assert_eq!(
factor.nrows(),
directions.nrows(),
"projected directional derivative traces require shared coefficient dimension"
);
Ok(None)
}
fn directional_derivative(
&self,
d_beta_flat: &Array1<f64>,
) -> Result<Option<Array2<f64>>, String>;
fn directional_derivative_operator(
&self,
d_beta_flat: &Array1<f64>,
) -> Result<Option<Arc<dyn HyperOperator>>, String> {
Ok(self
.directional_derivative(d_beta_flat)?
.map(|matrix| Arc::new(DenseMatrixHyperOperator { matrix }) as Arc<dyn HyperOperator>))
}
fn directional_derivative_operators(
&self,
d_beta_flats: &[Array1<f64>],
) -> Result<Vec<Option<Arc<dyn HyperOperator>>>, String> {
d_beta_flats
.iter()
.map(|d_beta_flat| self.directional_derivative_operator(d_beta_flat))
.collect()
}
fn second_directional_derivative(
&self,
arr: &Array1<f64>,
arr2: &Array1<f64>,
) -> Result<Option<Array2<f64>>, String> {
assert!(arr.iter().all(|v| !v.is_nan()));
assert!(arr2.iter().all(|v| !v.is_nan()));
Ok(None)
}
fn second_directional_derivative_operator(
&self,
d_beta_u: &Array1<f64>,
d_beta_v: &Array1<f64>,
) -> Result<Option<Arc<dyn HyperOperator>>, String> {
Ok(self
.second_directional_derivative(d_beta_u, d_beta_v)?
.map(|matrix| Arc::new(DenseMatrixHyperOperator { matrix }) as Arc<dyn HyperOperator>))
}
fn second_directional_derivative_operators(
&self,
d_beta_pairs: &[(Array1<f64>, Array1<f64>)],
) -> Result<Vec<Option<Arc<dyn HyperOperator>>>, String> {
d_beta_pairs
.iter()
.map(|(u, v)| self.second_directional_derivative_operator(u, v))
.collect()
}
}
pub use gam_problem::ExactNewtonJointPsiWorkspace;