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