use super::{Cone, JordanAlgebra, SymmetricCone, SymmetricConeUtils};
use crate::{
algebra::*,
solver::{core::ScalingStrategy, CoreSettings},
};
pub struct SecondOrderCone<T: FloatT = f64> {
dim: usize,
w: Vec<T>,
λ: Vec<T>,
pub u: Vec<T>,
pub v: Vec<T>,
d: T,
pub η: T,
}
impl<T> SecondOrderCone<T>
where
T: FloatT,
{
pub fn new(dim: usize) -> Self {
assert!(dim >= 2);
Self {
dim,
w: vec![T::zero(); dim],
λ: vec![T::zero(); dim],
u: vec![T::zero(); dim],
v: vec![T::zero(); dim],
d: T::one(),
η: T::zero(),
}
}
}
impl<T> Cone<T> for SecondOrderCone<T>
where
T: FloatT,
{
fn dim(&self) -> usize {
self.dim
}
fn degree(&self) -> usize {
1
}
fn numel(&self) -> usize {
self.dim()
}
fn is_symmetric(&self) -> bool {
true
}
fn rectify_equilibration(&self, δ: &mut [T], e: &[T]) -> bool {
δ.copy_from(e);
δ.reciprocal();
δ.scale(e.mean());
true }
fn shift_to_cone(&self, z: &mut [T]) {
z[0] = T::max(z[0], T::zero());
let α = _soc_residual(z);
if α < T::sqrt(T::epsilon()) {
z[0] -= α;
z[0] += T::one();
}
}
fn unit_initialization(&self, z: &mut [T], s: &mut [T]) {
z.fill(T::zero());
z.fill(T::zero());
self.add_scaled_e(z, T::one());
self.add_scaled_e(s, T::one());
}
fn set_identity_scaling(&mut self) {
self.d = T::one();
self.u.fill(T::zero());
self.v.fill(T::zero());
self.η = T::one();
self.w.fill(T::zero());
}
fn update_scaling(&mut self, s: &[T], z: &[T], _μ: T, _scaling_strategy: ScalingStrategy) {
let zscale = T::sqrt(_soc_residual(z));
let sscale = T::sqrt(_soc_residual(s));
let two = (2.0).as_T();
let half = (0.5).as_T();
let gamma = T::sqrt((T::one() + s.dot(z) / (zscale * sscale)) * half);
let w = &mut self.w;
w.copy_from(s);
w.scale(T::recip(two * sscale * gamma));
w[0] += z[0] / (two * zscale * gamma);
w[1..].axpby(-T::recip(two * zscale * gamma), &z[1..], T::one());
let w0p1 = w[0] + T::one();
let w1sq = w[1..].sumsq();
let w0sq = w[0] * w[0];
let α = w0p1 + w1sq / w0p1;
let β = T::one() + two / w0p1 + w1sq / (w0p1 * w0p1);
self.d = w0sq / two + w1sq / two * (T::one() - (α * α) / (T::one() + w1sq * β));
self.η = T::sqrt(sscale / zscale);
let u0 = T::sqrt(w0sq + w1sq - self.d);
let u1 = α / u0;
let v0 = T::zero();
let v1 = T::sqrt(u1 * u1 - β);
self.u[0] = u0;
self.u[1..].axpby(u1, &self.w[1..], T::zero());
self.v[0] = v0;
self.v[1..].axpby(v1, &self.w[1..], T::zero());
_soc_mul_W_inner(&mut self.λ, z, T::one(), T::zero(), &self.w, self.η);
}
fn Hs_is_diagonal(&self) -> bool {
true
}
fn get_Hs(&self, Hsblock: &mut [T]) {
Hsblock.fill(self.η * self.η);
Hsblock[0] *= self.d;
}
fn mul_Hs(&self, y: &mut [T], x: &[T], work: &mut [T]) {
self.mul_W(MatrixShape::N, work, x, T::one(), T::zero()); self.mul_W(MatrixShape::T, y, work, T::one(), T::zero()); }
fn affine_ds(&self, ds: &mut [T], _s: &[T]) {
self.circ_op(ds, &self.λ, &self.λ);
}
fn combined_ds_shift(&mut self, shift: &mut [T], step_z: &mut [T], step_s: &mut [T], σμ: T) {
self._combined_ds_shift_symmetric(shift, step_z, step_s, σμ);
}
fn Δs_from_Δz_offset(&self, out: &mut [T], ds: &[T], work: &mut [T]) {
self._Δs_from_Δz_offset_symmetric(out, ds, work);
}
fn step_length(
&self,
dz: &[T],
ds: &[T],
z: &[T],
s: &[T],
_settings: &CoreSettings<T>,
αmax: T,
) -> (T, T) {
let αz = _step_length_soc_component(z, dz, αmax);
let αs = _step_length_soc_component(s, ds, αmax);
(αz, αs)
}
fn compute_barrier(&self, z: &[T], s: &[T], dz: &[T], ds: &[T], α: T) -> T {
let res_s = _soc_residual_shifted(s, ds, α);
let res_z = _soc_residual_shifted(z, dz, α);
if res_s > T::zero() && res_z > T::zero() {
-(res_s * res_z).logsafe() * (0.5).as_T()
} else {
T::infinity()
}
}
}
impl<T> SymmetricCone<T> for SecondOrderCone<T>
where
T: FloatT,
{
fn λ_inv_circ_op(&self, x: &mut [T], z: &[T]) {
self.inv_circ_op(x, &self.λ, z);
}
fn add_scaled_e(&self, x: &mut [T], α: T) {
x[0] += α;
}
fn mul_W(&self, _is_transpose: MatrixShape, y: &mut [T], x: &[T], α: T, β: T) {
_soc_mul_W_inner(y, x, α, β, &self.w, self.η);
}
fn mul_Winv(&self, _is_transpose: MatrixShape, y: &mut [T], x: &[T], α: T, β: T) {
_soc_mul_Winv_inner(y, x, α, β, &self.w, self.η);
}
}
impl<T> JordanAlgebra<T> for SecondOrderCone<T>
where
T: FloatT,
{
fn circ_op(&self, x: &mut [T], y: &[T], z: &[T]) {
x[0] = y.dot(z);
let (y0, z0) = (y[0], z[0]);
x[1..].waxpby(y0, &z[1..], z0, &y[1..]);
}
fn inv_circ_op(&self, x: &mut [T], y: &[T], z: &[T]) {
let p = _soc_residual(y);
let pinv = T::recip(p);
let v = y[1..].dot(&z[1..]);
x[0] = (y[0] * z[0] - v) * pinv;
let c1 = pinv * (v / y[0] - z[0]);
let c2 = T::recip(y[0]);
x[1..].waxpby(c1, &y[1..], c2, &z[1..]);
}
}
fn _soc_residual<T>(z: &[T]) -> T
where
T: FloatT,
{
let (z1, z2) = (z[0], &z[1..]);
z1 * z1 - z2.sumsq()
}
fn _soc_residual_shifted<T>(z: &[T], dz: &[T], α: T) -> T
where
T: FloatT,
{
let sc = z[0] + α * dz[0];
let vpart = <[T] as VectorMath<T>>::dot_shifted(&z[1..], &z[1..], &dz[1..], &dz[1..], α);
sc * sc - vpart
}
fn _step_length_soc_component<T>(x: &[T], y: &[T], αmax: T) -> T
where
T: FloatT,
{
let two: T = (2.).as_T();
let four: T = (4.).as_T();
let a = _soc_residual(y);
let b = two * (x[0] * y[0] - x[1..].dot(&y[1..]));
let c = _soc_residual(x); let d = b * b - four * a * c;
if c < T::zero() {
panic!("starting point of line search not in SOC");
}
#[allow(clippy::if_same_then_else)] if (a > T::zero() && b > T::zero()) || d < T::zero() {
return αmax;
} else if a == T::zero() {
return αmax;
} else if c == T::zero() {
return if a >= T::zero() { αmax } else { T::zero() };
}
let t = {
if b >= T::zero() {
-b - T::sqrt(d)
} else {
-b + T::sqrt(d)
}
};
let r1: T = (two * c) / t;
let r2: T = t / (two * a);
let r1 = if r1 < T::zero() { T::infinity() } else { r1 };
let r2 = if r2 < T::zero() { T::infinity() } else { r2 };
T::min(αmax, T::min(r1, r2))
}
#[allow(non_snake_case)]
fn _soc_mul_W_inner<T>(y: &mut [T], x: &[T], α: T, β: T, w: &[T], η: T)
where
T: FloatT,
{
let ζ = w[1..].dot(&x[1..]);
let c = x[0] + ζ / (T::one() + w[0]);
y[0] = (α * η) * (w[0] * x[0] + ζ) + β * y[0];
y[1..].axpby(α * η * c, &w[1..], β);
y[1..].axpby(α * η, &x[1..], T::one());
}
fn _soc_mul_Winv_inner<T>(y: &mut [T], x: &[T], α: T, β: T, w: &[T], η: T)
where
T: FloatT,
{
let ζ = w[1..].dot(&x[1..]);
let c = -x[0] + ζ / (T::one() + w[0]);
y[0] = (α / η) * (w[0] * x[0] - ζ) + β * y[0];
y[1..].axpby(α / η * c, &w[1..], β);
y[1..].axpby(α / η, &x[1..], T::one());
}