Skip to main content

optimization_engine/constraints/
ballinf.rs

1use super::Constraint;
2use crate::FunctionCallResult;
3use num::Float;
4
5#[derive(Copy, Clone)]
6/// An infinity ball defined as $B_\infty^r = \\{x\in\mathbb{R}^n {}:{} \Vert{}x{}\Vert_{\infty} \leq r\\}$,
7/// where $\Vert{}\cdot{}\Vert_{\infty}$ is the infinity norm. The infinity ball centered at a point
8/// $x_c$ is defined as $B_\infty^{x_c,r} = \\{x\in\mathbb{R}^n {}:{} \Vert{}x-x_c{}\Vert_{\infty} \leq r\\}$.
9///
10pub struct BallInf<'a, T = f64> {
11    center: Option<&'a [T]>,
12    radius: T,
13}
14
15impl<'a, T: Float> BallInf<'a, T> {
16    /// Construct a new infinity-norm ball with given center and radius
17    /// If no `center` is given, then it is assumed to be in the origin
18    ///
19    /// # Example
20    ///
21    /// ```
22    /// use optimization_engine::constraints::{BallInf, Constraint};
23    ///
24    /// let ball = BallInf::new(None, 1.0);
25    /// let mut x = [2.0, -0.2, -3.0];
26    /// ball.project(&mut x).unwrap();
27    /// ```
28    pub fn new(center: Option<&'a [T]>, radius: T) -> Self {
29        assert!(radius > T::zero());
30        BallInf { center, radius }
31    }
32}
33
34impl<'a, T: Float> Constraint<T> for BallInf<'a, T> {
35    /// Computes the projection of a given vector `x` on the current infinity ball.
36    ///
37    ///
38    /// The projection of a $v\in\mathbb{R}^{n}$ on $B_\infty^r$ is given by
39    /// $\Pi_{B_\infty^r}(v) = z$ with
40    ///
41    /// $$
42    /// z_i = \begin{cases}v_i,&\text{ if } |z_i| \leq r\\\\\mathrm{sng}(v_i)r,&\text{ otherwise}\end{cases}
43    /// $$
44    ///
45    /// for all $i=1,\ldots, n$, where sgn is the sign function.
46    ///
47    /// The projection of $v\in\mathbb{R}^{n}$ on $B_\infty^{x_c,r}$ is given by
48    /// $\Pi_{B_\infty^r}(v) = z$ with
49    ///
50    /// $$
51    /// z_i = \begin{cases}v_i,&\text{ if } |z_i-x_{c, i}| \leq r\\\\x_{c,i} + \mathrm{sng}(v_i)r,&\text{ otherwise}\end{cases}
52    /// $$
53    ///
54    /// for all $i=1,\ldots, n$.
55    ///
56    fn project(&self, x: &mut [T]) -> FunctionCallResult {
57        if let Some(center) = &self.center {
58            assert_eq!(
59                x.len(),
60                center.len(),
61                "x and xc have incompatible dimensions"
62            );
63            x.iter_mut()
64                .zip(center.iter())
65                .filter(|(&mut xi, &ci)| (xi - ci).abs() > self.radius)
66                .for_each(|(xi, ci)| *xi = *ci + (*xi - *ci).signum() * self.radius);
67        } else {
68            x.iter_mut()
69                .filter(|xi| xi.abs() > self.radius)
70                .for_each(|xi| *xi = xi.signum() * self.radius);
71        }
72        Ok(())
73    }
74
75    fn is_convex(&self) -> bool {
76        true
77    }
78}