ellalgo_rs/
example1_rr.rs

1use super::cutting_plane::OracleOptim;
2use ndarray::prelude::*;
3
4type Arr = Array1<f64>;
5
6#[derive(Debug)]
7pub struct MyOracle {
8    /// for round robin
9    idx: i32,
10}
11
12impl Default for MyOracle {
13    fn default() -> Self {
14        MyOracle { idx: -1 }
15    }
16}
17
18impl OracleOptim<Arr> for MyOracle {
19    type CutChoice = f64; // single cut
20
21    /// The function assess_optim takes in two parameters, xc and gamma, and returns a tuple containing an
22    /// array and a double, along with a boolean value.
23    ///
24    /// Arguments:
25    ///
26    /// * `xc`: The parameter `xc` is an array of length 2, representing the values of `x` and `y`
27    ///         respectively.
28    /// * `gamma`: The parameter `gamma` is a mutable reference to a `f64` variable. It is used to store the
29    ///         current best solution for the optimization problem.
30    fn assess_optim(&mut self, xc: &Arr, gamma: &mut f64) -> ((Arr, f64), bool) {
31        let x = xc[0];
32        let y = xc[1];
33        let f0 = x + y;
34
35        let num_constraints = 3;
36        for _ in 0..num_constraints {
37            self.idx += 1;
38            if self.idx == num_constraints {
39                self.idx = 0; // round robin
40            }
41            let fj = match self.idx {
42                0 => f0 - 3.0,
43                1 => -x + y + 1.0,
44                2 => *gamma - f0,
45                _ => unreachable!(),
46            };
47            if fj > 0.0 {
48                return (
49                    (
50                        match self.idx {
51                            0 => array![1.0, 1.0],
52                            1 => array![-1.0, 1.0],
53                            2 => array![-1.0, -1.0],
54                            _ => unreachable!(),
55                        },
56                        fj,
57                    ),
58                    false,
59                );
60            }
61        }
62        *gamma = f0;
63        ((array![-1.0, -1.0], 0.0), true)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::cutting_plane::{cutting_plane_optim, Options};
71    use crate::ell::Ell;
72    // use ndarray::array;
73    // use super::ell_stable::EllStable;
74
75    /// Tests the feasibility of the optimization problem using the cutting plane method.
76    ///
77    /// This test creates a new `Ell` instance with a scalar radius of 10.0 and a center at `[0.0, 0.0]`.
78    /// It then creates a new `MyOracle` instance as the optimization oracle.
79    /// The `gamma` variable is initialized to negative infinity.
80    /// The `Options` struct is configured with a tolerance of 1e-10.
81    /// The `cutting_plane_optim` function is called with the oracle, ellipsoid, gamma, and options.
82    /// The test asserts that the best solution `xbest` is not `None`, and that the number of iterations is 25.
83    #[test]
84    pub fn test_feasible() {
85        let mut ellip = Ell::new_with_scalar(10.0, array![0.0, 0.0]);
86        let mut oracle = MyOracle::default();
87        let mut gamma = f64::NEG_INFINITY;
88        let options = Options {
89            tolerance: 1e-10,
90            ..Default::default()
91        };
92        let (xbest, num_iters) = cutting_plane_optim(&mut oracle, &mut ellip, &mut gamma, &options);
93        assert!(xbest.is_some());
94        assert_eq!(num_iters, 25);
95    }
96
97    #[test]
98    pub fn test_infeasible1() {
99        let mut ellip = Ell::new(array![10.0, 10.0], array![100.0, 100.0]); // wrong initial guess
100                                                                            // or ellipsoid is too small
101        let mut oracle = MyOracle::default();
102        let mut gamma = f64::NEG_INFINITY;
103        let options = Options::default();
104        let (xbest, _num_iters) =
105            cutting_plane_optim(&mut oracle, &mut ellip, &mut gamma, &options);
106        assert!(xbest.is_none());
107    }
108
109    #[test]
110    pub fn test_infeasible2() {
111        let mut ellip = Ell::new(array![10.0, 10.0], array![0.0, 0.0]);
112        let mut oracle = MyOracle::default();
113        // wrong initial guess
114        let options = Options::default();
115        let (xbest, _niter) = cutting_plane_optim(&mut oracle, &mut ellip, &mut 100.0, &options);
116        assert!(xbest.is_none());
117    }
118}