use super::*;
#[cfg(feature = "sdp")]
use crate::algebra::triangular_number;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum SupportedConeT<T> {
ZeroConeT(usize),
NonnegativeConeT(usize),
SecondOrderConeT(usize),
ExponentialConeT(),
PowerConeT(T),
GenPowerConeT(Vec<T>, usize),
#[cfg(feature = "sdp")]
PSDTriangleConeT(usize),
}
impl<T> SupportedConeT<T> {
pub(crate) fn nvars(&self) -> usize {
match self {
SupportedConeT::ZeroConeT(dim) => *dim,
SupportedConeT::NonnegativeConeT(dim) => *dim,
SupportedConeT::SecondOrderConeT(dim) => *dim,
SupportedConeT::ExponentialConeT() => 3,
SupportedConeT::PowerConeT(_) => 3,
#[cfg(feature = "sdp")]
SupportedConeT::PSDTriangleConeT(dim) => triangular_number(*dim),
SupportedConeT::GenPowerConeT(α, dim2) => α.len() + *dim2,
}
}
}
impl<T> std::fmt::Display for SupportedConeT<T>
where
T: FloatT,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", &self.as_tag().as_str())
}
}
pub fn make_cone<T: FloatT>(cone: &SupportedConeT<T>) -> SupportedCone<T> {
match cone {
SupportedConeT::NonnegativeConeT(dim) => NonnegativeCone::<T>::new(*dim).into(),
SupportedConeT::ZeroConeT(dim) => ZeroCone::<T>::new(*dim).into(),
SupportedConeT::SecondOrderConeT(dim) => SecondOrderCone::<T>::new(*dim).into(),
SupportedConeT::ExponentialConeT() => ExponentialCone::<T>::new().into(),
SupportedConeT::PowerConeT(α) => PowerCone::<T>::new(*α).into(),
SupportedConeT::GenPowerConeT(α, dim2) => {
GenPowerCone::<T>::new((*α).clone(), *dim2).into()
}
#[cfg(feature = "sdp")]
SupportedConeT::PSDTriangleConeT(dim) => PSDTriangleCone::<T>::new(*dim).into(),
}
}
impl<T> SupportedConeT<T>
where
T: FloatT,
{
pub(crate) fn new_collapsed(cones: &[SupportedConeT<T>]) -> Vec<SupportedConeT<T>> {
let mut newcones = Vec::with_capacity(cones.len());
let mut iter = cones.iter().peekable();
fn collapse<T>(
iter: &mut std::iter::Peekable<std::slice::Iter<'_, SupportedConeT<T>>>,
newcones: &mut Vec<SupportedConeT<T>>,
init_dim: usize,
) where
T: FloatT,
{
let mut total_dim = init_dim;
while let Some(next_cone) = iter.peek() {
if next_cone.nvars() != 0 {
match next_cone {
SupportedConeT::NonnegativeConeT(dim) => total_dim += dim,
SupportedConeT::SecondOrderConeT(1) => total_dim += 1,
#[cfg(feature = "sdp")]
SupportedConeT::PSDTriangleConeT(1) => total_dim += 1,
_ => break,
}
}
iter.next();
}
newcones.push(SupportedConeT::NonnegativeConeT(total_dim));
}
while let Some(cone) = iter.next() {
if cone.nvars() != 0 {
match cone {
SupportedConeT::NonnegativeConeT(dim) => {
collapse(&mut iter, &mut newcones, *dim)
}
SupportedConeT::SecondOrderConeT(dim) if *dim == 1 => {
collapse(&mut iter, &mut newcones, *dim)
}
#[cfg(feature = "sdp")]
SupportedConeT::PSDTriangleConeT(dim) if *dim == 1 => {
collapse(&mut iter, &mut newcones, *dim)
}
_ => newcones.push(cone.clone()),
}
}
}
newcones.shrink_to_fit();
newcones
}
}
#[allow(clippy::enum_variant_names)]
#[enum_dispatch(Cone<T>)]
pub enum SupportedCone<T>
where
T: FloatT,
{
ZeroCone(ZeroCone<T>),
NonnegativeCone(NonnegativeCone<T>),
SecondOrderCone(SecondOrderCone<T>),
ExponentialCone(ExponentialCone<T>),
PowerCone(PowerCone<T>),
GenPowerCone(GenPowerCone<T>),
#[cfg(feature = "sdp")]
PSDTriangleCone(PSDTriangleCone<T>),
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub(crate) enum SupportedConeTag {
ZeroCone = 0,
NonnegativeCone,
SecondOrderCone,
ExponentialCone,
PowerCone,
GenPowerCone,
#[cfg(feature = "sdp")]
PSDTriangleCone,
}
pub(crate) trait SupportedConeAsTag {
fn as_tag(&self) -> SupportedConeTag;
}
impl<T> SupportedConeAsTag for SupportedConeT<T> {
fn as_tag(&self) -> SupportedConeTag {
match self {
SupportedConeT::NonnegativeConeT(_) => SupportedConeTag::NonnegativeCone,
SupportedConeT::ZeroConeT(_) => SupportedConeTag::ZeroCone,
SupportedConeT::SecondOrderConeT(_) => SupportedConeTag::SecondOrderCone,
SupportedConeT::ExponentialConeT() => SupportedConeTag::ExponentialCone,
SupportedConeT::PowerConeT(_) => SupportedConeTag::PowerCone,
#[cfg(feature = "sdp")]
SupportedConeT::PSDTriangleConeT(_) => SupportedConeTag::PSDTriangleCone,
SupportedConeT::GenPowerConeT(_, _) => SupportedConeTag::GenPowerCone,
}
}
}
impl<T: FloatT> SupportedConeAsTag for SupportedCone<T> {
fn as_tag(&self) -> SupportedConeTag {
match self {
SupportedCone::NonnegativeCone(_) => SupportedConeTag::NonnegativeCone,
SupportedCone::ZeroCone(_) => SupportedConeTag::ZeroCone,
SupportedCone::SecondOrderCone(_) => SupportedConeTag::SecondOrderCone,
SupportedCone::ExponentialCone(_) => SupportedConeTag::ExponentialCone,
SupportedCone::PowerCone(_) => SupportedConeTag::PowerCone,
#[cfg(feature = "sdp")]
SupportedCone::PSDTriangleCone(_) => SupportedConeTag::PSDTriangleCone,
SupportedCone::GenPowerCone(_) => SupportedConeTag::GenPowerCone,
}
}
}
impl SupportedConeTag {
pub fn as_str(&self) -> &'static str {
match self {
SupportedConeTag::ZeroCone => "ZeroCone",
SupportedConeTag::NonnegativeCone => "NonnegativeCone",
SupportedConeTag::SecondOrderCone => "SecondOrderCone",
SupportedConeTag::ExponentialCone => "ExponentialCone",
SupportedConeTag::PowerCone => "PowerCone",
#[cfg(feature = "sdp")]
SupportedConeTag::PSDTriangleCone => "PSDTriangleCone",
SupportedConeTag::GenPowerCone => "GenPowerCone",
}
}
}
#[cfg_attr(not(feature = "sdp"), allow(dead_code))]
pub(crate) struct RangeSupportedConesIterator<'a, T> {
cones: &'a [SupportedConeT<T>],
index: usize,
start: usize,
}
#[cfg_attr(not(feature = "sdp"), allow(dead_code))]
impl<T> Iterator for RangeSupportedConesIterator<'_, T> {
type Item = std::ops::Range<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.cones.len() {
let cone = &self.cones[self.index];
let stop = self.start + cone.nvars();
let range = self.start..stop;
self.index += 1;
self.start = stop;
Some(range)
} else {
None
}
}
}
#[cfg_attr(not(feature = "sdp"), allow(dead_code))]
pub(crate) trait ConeRanges<'a, T> {
fn rng_cones_iter(&'a self) -> RangeSupportedConesIterator<'a, T>;
}
#[cfg_attr(not(feature = "sdp"), allow(dead_code))]
impl<'a, T> ConeRanges<'a, T> for [SupportedConeT<T>] {
fn rng_cones_iter(&'a self) -> RangeSupportedConesIterator<'a, T> {
RangeSupportedConesIterator::<'a, T> {
cones: self,
index: 0,
start: 0,
}
}
}
#[test]
fn test_cone_ranges() {
let cones = [
SupportedConeT::NonnegativeConeT::<f64>(3),
SupportedConeT::NonnegativeConeT::<f64>(0),
SupportedConeT::SecondOrderConeT::<f64>(4),
];
let rngs: Vec<std::ops::Range<usize>> = vec![0..3, 3..3, 3..7];
for (rng, conerng) in std::iter::zip(rngs.iter(), cones.rng_cones_iter()) {
assert_eq!(*rng, conerng);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_collapsed_no_changes() {
let cones = vec![
SupportedConeT::<f64>::NonnegativeConeT(3),
SupportedConeT::SecondOrderConeT(4),
SupportedConeT::ExponentialConeT(),
];
let expected = cones.clone();
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[test]
fn test_new_collapsed_consolidate_nonnegative() {
let cones = vec![
SupportedConeT::<f64>::NonnegativeConeT(3),
SupportedConeT::NonnegativeConeT(2),
SupportedConeT::SecondOrderConeT(4),
];
let expected = vec![
SupportedConeT::<f64>::NonnegativeConeT(5),
SupportedConeT::SecondOrderConeT(4),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[test]
fn test_new_collapsed_remove_empty() {
let cones = vec![
SupportedConeT::<f64>::NonnegativeConeT(3),
SupportedConeT::ZeroConeT(0),
SupportedConeT::SecondOrderConeT(4),
SupportedConeT::NonnegativeConeT(0),
];
let expected = vec![
SupportedConeT::NonnegativeConeT(3),
SupportedConeT::SecondOrderConeT(4),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[test]
fn test_new_collapsed_soc_to_nonnegative() {
let cones = vec![
SupportedConeT::<f64>::SecondOrderConeT(1),
SupportedConeT::SecondOrderConeT(4),
];
let expected = vec![
SupportedConeT::NonnegativeConeT(1),
SupportedConeT::SecondOrderConeT(4),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[cfg(feature = "sdp")]
#[test]
fn test_new_collapsed_psd_to_nonnegative() {
let cones = vec![
SupportedConeT::<f64>::PSDTriangleConeT(1),
SupportedConeT::SecondOrderConeT(4),
];
let expected = vec![
SupportedConeT::NonnegativeConeT(1),
SupportedConeT::SecondOrderConeT(4),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[test]
fn test_new_collapsed_mixed() {
let cones = vec![
SupportedConeT::<f64>::SecondOrderConeT(1),
SupportedConeT::NonnegativeConeT(3),
SupportedConeT::NonnegativeConeT(2),
SupportedConeT::ExponentialConeT(),
SupportedConeT::NonnegativeConeT(0),
SupportedConeT::SecondOrderConeT(1),
];
let expected = vec![
SupportedConeT::NonnegativeConeT(6),
SupportedConeT::ExponentialConeT(),
SupportedConeT::NonnegativeConeT(1),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
#[cfg(feature = "sdp")]
#[test]
fn test_new_collapsed_mixed_sdp() {
let cones = vec![
SupportedConeT::<f64>::NonnegativeConeT(3),
SupportedConeT::NonnegativeConeT(2),
SupportedConeT::ZeroConeT(0),
SupportedConeT::SecondOrderConeT(1),
SupportedConeT::PSDTriangleConeT(1),
SupportedConeT::SecondOrderConeT(4),
SupportedConeT::NonnegativeConeT(0),
];
let expected = vec![
SupportedConeT::NonnegativeConeT(7),
SupportedConeT::SecondOrderConeT(4),
];
let result = SupportedConeT::new_collapsed(&cones);
assert_eq!(result, expected);
}
}