use crate::{algebra::*, solver::core::ScalingStrategy};
pub(crate) trait NonsymmetricCone<T: FloatT> {
fn is_primal_feasible(&self, s: &[T]) -> bool;
fn is_dual_feasible(&self, z: &[T]) -> bool;
fn barrier_primal(&mut self, s: &[T]) -> T;
fn barrier_dual(&mut self, z: &[T]) -> T;
fn higher_correction(&mut self, η: &mut [T], ds: &[T], v: &[T]);
fn update_dual_grad_H(&mut self, z: &[T]);
}
#[allow(clippy::too_many_arguments)]
pub(crate) trait Nonsymmetric3DCone<T: FloatT> {
fn gradient_primal(&self, s: &[T]) -> [T; 3];
fn split_borrow_mut(
&mut self,
) -> (
&mut DenseMatrixSym3<T>,
&mut DenseMatrixSym3<T>,
&mut [T; 3],
&mut [T; 3],
);
}
pub(crate) trait Nonsymmetric3DConeUtils<T: FloatT> {
fn update_Hs(&mut self, s: &[T], z: &[T], μ: T, scaling_strategy: ScalingStrategy);
fn use_dual_scaling(&mut self, μ: T);
fn use_primal_dual_scaling(&mut self, s: &[T], z: &[T]);
}
impl<T, C> Nonsymmetric3DConeUtils<T> for C
where
T: FloatT,
C: Nonsymmetric3DCone<T>,
{
fn update_Hs(&mut self, s: &[T], z: &[T], μ: T, scaling_strategy: ScalingStrategy) {
if scaling_strategy == ScalingStrategy::Dual {
self.use_dual_scaling(μ);
} else {
self.use_primal_dual_scaling(s, z);
}
}
fn use_dual_scaling(&mut self, μ: T) {
let (H_dual, Hs, _, _) = self.split_borrow_mut();
Hs.scaled_from(μ, H_dual);
}
fn use_primal_dual_scaling(&mut self, s: &[T], z: &[T]) {
let three: T = (3.).as_T();
let zt: [T; 3] = self.gradient_primal(s);
let (H_dual, Hs, grad, _) = self.split_borrow_mut();
let st = grad;
let mut δs = [T::zero(); 3];
let mut tmp = [T::zero(); 3];
let dot_sz = s.dot(z);
let μ = dot_sz / three;
let μt = st[..].dot(&zt[..]) / three;
let mut δz = tmp;
for i in 0..3 {
δs[i] = s[i] + μ * st[i];
δz[i] = z[i] + μ * zt[i];
}
let dot_δsz = δs[..].dot(&δz[..]);
let de1 = μ * μt - T::one();
let de2 = H_dual.quad_form(&zt, &zt) - three * μt * μt;
if T::abs(de1) > T::sqrt(T::epsilon()) && T::abs(de2) > T::epsilon() && dot_sz > T::zero() &&
dot_δsz > T::zero()
{
H_dual.mul(&mut tmp, &zt);
for i in 0..3 {
tmp[i] = μt * st[i] - tmp[i];
}
Hs.copy_from(H_dual);
for i in 0..3 {
for j in i..3 {
Hs[(i, j)] -= st[i] * st[j] / three + tmp[i] * tmp[j] / de2;
}
}
let t = μ * Hs.norm_fro();
let mut axis_z = tmp;
axis_z[0] = z[1] * zt[2] - z[2] * zt[1];
axis_z[1] = z[2] * zt[0] - z[0] * zt[2];
axis_z[2] = z[0] * zt[1] - z[1] * zt[0];
axis_z.normalize();
for i in 0..3 {
for j in i..3 {
Hs[(i, j)] =
s[i] * s[j] / dot_sz + δs[i] * δs[j] / dot_δsz + t * axis_z[i] * axis_z[j];
}
}
} else {
self.use_dual_scaling(μ);
}
}
}
pub(crate) trait NonsymmetricNDCone<T: FloatT> {
fn gradient_primal(&self, grad: &mut [T], s: &[T]);
}
pub(crate) fn backtrack_search<T>(
dq: &[T],
q: &[T],
α_init: T,
α_min: T,
step: T,
is_in_cone_fcn: impl Fn(&[T]) -> bool,
work: &mut [T],
) -> T
where
T: FloatT,
{
let mut α = α_init;
loop {
work.waxpby(T::one(), q, α, dq);
if is_in_cone_fcn(work) {
break;
}
α *= step;
if α < α_min {
α = T::zero();
break;
}
}
α
}
pub(crate) fn newton_raphson_onesided<T>(x0: T, f0: impl Fn(T) -> T, f1: impl Fn(T) -> T) -> T
where
T: FloatT,
{
let mut x = x0;
let mut iter = 0;
while iter < 100 {
iter += 1;
let dfdx = f1(x);
let dx = -f0(x) / dfdx;
if (dx < T::epsilon())
|| (T::abs(dx / x) < T::sqrt(T::epsilon()))
|| (T::abs(dfdx) < T::epsilon())
{
break;
}
x += dx;
}
x
}