use ndarray::{Array1, Array2, ArrayBase, Data, DataMut, Dimension};
use rand::{Rng, RngExt};
use rand_distr::{Distribution, StandardNormal, uniform::SampleUniform};
use super::Scalar;
use super::cl_scaling::{
BoxAffineScaling, cl_scaling_pair, max_feasible_step_component,
project_strictly_inside_component,
};
use super::sample::{SampleStandardNormal, SampleUniformBox};
use super::{
ClampInPlace, ComponentDivAssign, ComponentMaxAssign, ComponentMulAssign, Dot,
FloorZerosInPlace, MatDiagonal, MatTransposeVec, MatVec, MatrixFromDiagonal, MatrixIdentity,
NegInPlace, NormInfinity, NormSquared, RankOneUpdate, ScaleInPlace, ScaledAdd, SymmetricEigen,
SymmetricEigenError, VectorIndex, VectorLen,
};
impl<F, S, D> ScaledAdd<F> for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn scaled_add(&mut self, scalar: F, other: &Self) {
assert_eq!(self.shape(), other.shape(), "scaled_add: shape mismatch");
self.zip_mut_with(other, |x, y| *x = *x + scalar * *y);
}
}
impl<F, S, D> NormSquared<F> for ArrayBase<S, D>
where
F: Scalar,
S: Data<Elem = F>,
D: Dimension,
{
fn norm_squared(&self) -> F {
self.iter().map(|x| *x * *x).sum()
}
}
impl<F, S, D> NormInfinity<F> for ArrayBase<S, D>
where
F: Scalar,
S: Data<Elem = F>,
D: Dimension,
{
fn norm_infinity(&self) -> F {
self.iter().map(|x| x.abs()).fold(F::zero(), F::max)
}
}
impl<F, S, D> Dot<F> for ArrayBase<S, D>
where
F: Scalar,
S: Data<Elem = F>,
D: Dimension,
{
fn dot(&self, other: &Self) -> F {
assert_eq!(self.shape(), other.shape(), "dot: shape mismatch");
self.iter().zip(other.iter()).map(|(a, b)| *a * *b).sum()
}
}
impl<F, S, D> NegInPlace for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn neg_in_place(&mut self) {
self.map_inplace(|x| *x = -*x);
}
}
impl<F: Scalar + SampleUniform> SampleUniformBox for Array1<F> {
fn sample_uniform_box<R: Rng + ?Sized>(lower: &Self, upper: &Self, rng: &mut R) -> Self {
assert_eq!(
lower.len(),
upper.len(),
"sample_uniform_box: bounds length mismatch"
);
Array1::from_shape_fn(lower.len(), |i| rng.random_range(lower[i]..=upper[i]))
}
}
impl<F: Scalar> VectorLen for Array1<F> {
fn vec_len(&self) -> usize {
self.len()
}
}
impl<F: Scalar> MatVec<Array1<F>> for Array2<F> {
fn matvec(&self, x: &Array1<F>) -> Array1<F> {
assert_eq!(
x.len(),
self.ncols(),
"matvec: x has length {} but the matrix has {} columns",
x.len(),
self.ncols()
);
Array1::from_shape_fn(self.nrows(), |i| {
self.row(i)
.iter()
.zip(x.iter())
.map(|(a, xj)| *a * *xj)
.sum()
})
}
}
impl<F: Scalar> MatTransposeVec<Array1<F>> for Array2<F> {
fn mat_transpose_vec(&self, x: &Array1<F>) -> Array1<F> {
assert_eq!(
x.len(),
self.nrows(),
"mat_transpose_vec: x has length {} but the matrix has {} rows",
x.len(),
self.nrows()
);
Array1::from_shape_fn(self.ncols(), |j| {
self.column(j)
.iter()
.zip(x.iter())
.map(|(a, xi)| *a * *xi)
.sum()
})
}
}
impl<F: Scalar> MatrixIdentity for Array2<F> {
fn identity(n: usize) -> Self {
Array2::eye(n)
}
}
impl<F: Scalar> MatrixFromDiagonal<Array1<F>> for Array2<F> {
fn from_diagonal(diag: &Array1<F>) -> Self {
Array2::from_diag(diag)
}
}
impl<F: Scalar> MatDiagonal<Array1<F>> for Array2<F> {
fn diagonal(&self) -> Array1<F> {
assert_eq!(
self.nrows(),
self.ncols(),
"diagonal: matrix must be square, got {}x{}",
self.nrows(),
self.ncols()
);
self.diag().to_owned()
}
}
impl<F: Scalar> RankOneUpdate<Array1<F>, F> for Array2<F> {
fn rank_one_update(&mut self, alpha: F, v: &Array1<F>) {
assert_eq!(
self.nrows(),
self.ncols(),
"rank_one_update: matrix must be square, got {}x{}",
self.nrows(),
self.ncols()
);
assert_eq!(
self.nrows(),
v.len(),
"rank_one_update: matrix is {}x{} but v has length {}",
self.nrows(),
self.ncols(),
v.len()
);
let n = self.nrows();
for i in 0..n {
let av = alpha * v[i];
let mut row = self.row_mut(i);
for j in 0..n {
let entry = &mut row[j];
*entry = *entry + av * v[j];
}
}
}
}
impl<F: Scalar> SymmetricEigen<Array1<F>> for Array2<F> {
fn try_eigh(&self) -> Result<(Self, Array1<F>), SymmetricEigenError> {
assert_eq!(
self.nrows(),
self.ncols(),
"try_eigh: matrix must be square, got {}x{}",
self.nrows(),
self.ncols()
);
let n = self.nrows();
let standard = self.as_standard_layout();
let slice = standard
.as_slice()
.expect("as_standard_layout produces a contiguous row-major slice");
let (eigenvalues, eigenvectors) =
super::dense_eig::jacobi_eigen(slice, n).ok_or(SymmetricEigenError::Failed)?;
let b =
Array2::from_shape_vec((n, n), eigenvectors).expect("jacobi_eigen returns n*n entries");
Ok((b, Array1::from_vec(eigenvalues)))
}
}
impl<F: Scalar> VectorIndex<F> for Array1<F> {
fn get_scalar(&self, i: usize) -> F {
self[i]
}
fn set_scalar(&mut self, i: usize, value: F) {
self[i] = value;
}
}
impl<F: Scalar> SampleStandardNormal for Array1<F>
where
StandardNormal: Distribution<F>,
{
fn sample_standard_normal<R: Rng + ?Sized>(template: &Self, rng: &mut R) -> Self {
Array1::from_shape_fn(template.len(), |_| StandardNormal.sample(rng))
}
}
impl<F, S, D> ScaleInPlace<F> for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn scale_in_place(&mut self, scalar: F) {
self.map_inplace(|x| *x = *x * scalar);
}
}
impl<F, S, D> ComponentMulAssign for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn component_mul_assign(&mut self, other: &Self) {
assert_eq!(
self.shape(),
other.shape(),
"component_mul_assign: shape mismatch"
);
self.zip_mut_with(other, |x, y| *x = *x * *y);
}
}
impl<F, S, D> ComponentMaxAssign for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn component_max_assign(&mut self, other: &Self) {
assert_eq!(
self.shape(),
other.shape(),
"component_max_assign: shape mismatch"
);
self.zip_mut_with(other, |x, y| *x = x.max(*y));
}
}
impl<F, S, D> FloorZerosInPlace<F> for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn floor_zeros_in_place(&mut self, value: F) {
self.map_inplace(|x| {
if *x <= F::zero() {
*x = value;
}
});
}
}
impl<F, S, D> ComponentDivAssign for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn component_div_assign(&mut self, other: &Self) {
assert_eq!(
self.shape(),
other.shape(),
"component_div_assign: shape mismatch"
);
self.zip_mut_with(other, |x, y| *x = *x / *y);
}
}
impl<F, S, D> ClampInPlace for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn clamp_in_place(&mut self, lower: &Self, upper: &Self) {
assert_eq!(
self.shape(),
lower.shape(),
"clamp_in_place: lower shape mismatch"
);
assert_eq!(
self.shape(),
upper.shape(),
"clamp_in_place: upper shape mismatch"
);
ndarray::Zip::from(self)
.and(lower)
.and(upper)
.for_each(|x, &lo, &hi| *x = (*x).max(lo).min(hi));
}
}
impl<F, S, D> BoxAffineScaling<F> for ArrayBase<S, D>
where
F: Scalar,
S: DataMut<Elem = F>,
D: Dimension,
{
fn compute_cl_scaling(
&self,
gradient: &Self,
lower: &Self,
upper: &Self,
d_sq: &mut Self,
c_diag: &mut Self,
) {
assert_eq!(
self.shape(),
gradient.shape(),
"compute_cl_scaling: gradient shape mismatch"
);
assert_eq!(
self.shape(),
lower.shape(),
"compute_cl_scaling: lower shape mismatch"
);
assert_eq!(
self.shape(),
upper.shape(),
"compute_cl_scaling: upper shape mismatch"
);
assert_eq!(
self.shape(),
d_sq.shape(),
"compute_cl_scaling: d_sq shape mismatch"
);
assert_eq!(
self.shape(),
c_diag.shape(),
"compute_cl_scaling: c_diag shape mismatch"
);
ndarray::Zip::from(d_sq)
.and(c_diag)
.and(self)
.and(gradient)
.and(lower)
.and(upper)
.for_each(|d, c, &x, &g, &l, &u| {
let (d_sq_i, c_i) = cl_scaling_pair::<F>(x, g, l, u);
*d = d_sq_i;
*c = c_i;
});
}
fn max_feasible_step(&self, step: &Self, lower: &Self, upper: &Self) -> F {
assert_eq!(
self.shape(),
step.shape(),
"max_feasible_step: step shape mismatch"
);
assert_eq!(
self.shape(),
lower.shape(),
"max_feasible_step: lower shape mismatch"
);
assert_eq!(
self.shape(),
upper.shape(),
"max_feasible_step: upper shape mismatch"
);
let mut tau = F::infinity();
ndarray::Zip::from(self)
.and(step)
.and(lower)
.and(upper)
.for_each(|&x, &s, &l, &u| {
let t = max_feasible_step_component::<F>(x, s, l, u);
if t < tau {
tau = t;
}
});
tau
}
fn cl_kkt_inf_norm(&self, d_sq: &Self) -> F {
assert_eq!(
self.shape(),
d_sq.shape(),
"cl_kkt_inf_norm: shape mismatch"
);
let mut best = F::zero();
ndarray::Zip::from(self).and(d_sq).for_each(|&v, &d| {
let candidate = <F as num_traits::Float>::abs(v) / d;
if candidate > best {
best = candidate;
}
});
best
}
fn weighted_norm_squared(&self, weights: &Self) -> F {
assert_eq!(
self.shape(),
weights.shape(),
"weighted_norm_squared: shape mismatch"
);
let mut sum = F::zero();
ndarray::Zip::from(self)
.and(weights)
.for_each(|&v, &w| sum = sum + v * v * w);
sum
}
fn project_strictly_inside(&mut self, lower: &Self, upper: &Self, rstep: F) {
assert_eq!(
self.shape(),
lower.shape(),
"project_strictly_inside: lower shape mismatch"
);
assert_eq!(
self.shape(),
upper.shape(),
"project_strictly_inside: upper shape mismatch"
);
ndarray::Zip::from(self)
.and(lower)
.and(upper)
.for_each(|x, &l, &u| {
*x = project_strictly_inside_component::<F>(*x, l, u, rstep);
});
}
}