Thanks to visit codestin.com
Credit goes to docs.rs

optimization_engine/constraints/
sphere2.rs

1use super::Constraint;
2
3#[derive(Copy, Clone)]
4/// A Euclidean sphere, that is, a set given by $S_2^r = \\{x \in \mathbb{R}^n {}:{} \Vert{}x{}\Vert = r\\}$
5/// or a Euclidean sphere centered at a point $x_c$, that is, $S_2^{x_c, r} = \\{x \in \mathbb{R}^n {}:{} \Vert{}x-x_c{}\Vert = r\\}$
6pub struct Sphere2<'a> {
7    center: Option<&'a [f64]>,
8    radius: f64,
9}
10
11impl<'a> Sphere2<'a> {
12    /// Construct a new Euclidean sphere with given center and radius
13    /// If no `center` is given, then it is assumed to be in the origin
14    pub fn new(center: Option<&'a [f64]>, radius: f64) -> Self {
15        assert!(radius > 0.0);
16        Sphere2 { center, radius }
17    }
18}
19
20impl<'a> Constraint for Sphere2<'a> {
21    /// Projection onto the sphere, $S_{r, c}$ with radius $r$ and center $c$.
22    /// If $x\neq c$, the projection is uniquely defined by
23    ///
24    /// $$
25    /// P_{S_{r, c}}(x) = c + r\frac{x-c}{\Vert{}x-c\Vert_2},
26    /// $$
27    ///
28    /// but for $x=c$, the projection is multi-valued. In particular, let
29    /// $y = P_{S_{r, c}}(c)$. Then $y_1 = c_1 + r$ and $y_i = c_i$ for
30    /// $i=2,\ldots, n$.
31    ///
32    /// ## Arguments
33    ///
34    /// - `x`: The given vector $x$ is updated with the projection on the set
35    ///
36    fn project(&self, x: &mut [f64]) {
37        let epsilon = 1e-12;
38        if let Some(center) = &self.center {
39            let norm_difference = crate::matrix_operations::norm2_squared_diff(x, center).sqrt();
40            if norm_difference <= epsilon {
41                x.copy_from_slice(center);
42                x[0] += self.radius;
43                return;
44            }
45            x.iter_mut().zip(center.iter()).for_each(|(x, c)| {
46                *x = *c + self.radius * (*x - *c) / norm_difference;
47            });
48        } else {
49            let norm_x = crate::matrix_operations::norm2(x);
50            if norm_x <= epsilon {
51                x[0] += self.radius;
52                return;
53            }
54            let norm_over_radius = self.radius / norm_x;
55            x.iter_mut().for_each(|x_| *x_ *= norm_over_radius);
56        }
57    }
58
59    /// Returns false (the sphere is not a convex set)
60    ///
61    fn is_convex(&self) -> bool {
62        false
63    }
64}