ellalgo_rs/
ell_calc.rs

1// mod lib;
2use crate::cutting_plane::CutStatus;
3
4/// The `EllCalcCore` struct represents the parameters for calculating the new Ellipsoid Search Space.
5///
6/// Properties:
7///
8/// * `n_f`: The `n_f` property represents the number of variables in the ellipsoid search space.
9/// * `n_plus_1`: The `n_plus_1` property represents the value of `n + 1`, where `n` is the dimension of
10///             the search space. It is used in calculations related to the ellipsoid search space.
11/// * `half_n`: The `half_n` property represents half of the dimension of the ellipsoid search space. It
12///             is used in the calculation of the parameters for the ellipsoid search space.
13/// * `cst1`: The `cst1` property is a constant used in the calculation of the parameters for the new
14///             Ellipsoid Search Space. Its specific purpose and value are not provided in the code snippet.
15/// * `cst2`: The `cst2` property is a constant used in the calculation of the parameters for the new
16///             Ellipsoid Search Space. It is not specified what exactly it represents or how it is used in the
17///             calculation.
18/// * `cst3`: The `cst3` property is a constant value used in the calculation of the parameters for the
19///             new Ellipsoid Search Space. It is not specified what exactly this constant represents or how it is
20///             used in the calculations.
21#[derive(Debug, Clone)]
22pub struct EllCalcCore {
23    pub n_f: f64,
24    pub n_plus_1: f64,
25    pub half_n: f64,
26    pub inv_n: f64,
27    cst1: f64,
28    cst2: f64,
29}
30
31impl EllCalcCore {
32    /// Constructs a new EllCalcCore instance, initializing its fields based on the provided dimension n_f.
33    ///
34    /// This is a constructor function for the EllCalcCore struct. It takes in the dimension n_f and uses
35    /// it to calculate and initialize the other fields of EllCalcCore that depend on n_f.
36    ///
37    /// Being a constructor function, it is part of the public API of the EllCalcCore struct. The
38    /// documentation follows the conventions and style of the other documentation in this crate.
39    ///
40    /// Arguments:
41    ///
42    /// * `n_f`: The parameter `n_f` represents the value of `n` in the calculations. It is a floating-point
43    ///          number.
44    ///
45    /// Returns:
46    ///
47    /// The `new` function returns an instance of the `EllCalcCore` struct.
48    ///
49    /// # Examples:
50    ///
51    /// ```rust
52    /// use approx_eq::assert_approx_eq;
53    /// use ellalgo_rs::ell_calc::EllCalcCore;
54    ///
55    /// let ell_calc_core = EllCalcCore::new(4.0);
56    ///
57    /// assert_approx_eq!(ell_calc_core.n_f, 4.0);
58    /// assert_approx_eq!(ell_calc_core.half_n, 2.0);
59    /// assert_approx_eq!(ell_calc_core.n_plus_1, 5.0);
60    /// ```
61    pub fn new(n_f: f64) -> EllCalcCore {
62        let n_plus_1 = n_f + 1.0;
63        let half_n = n_f / 2.0;
64        let inv_n = 1.0 / n_f;
65        let n_sq = n_f * n_f;
66        let cst0 = 1.0 / (n_f + 1.0);
67        let cst1 = n_sq / (n_sq - 1.0);
68        let cst2 = 2.0 * cst0;
69
70        EllCalcCore {
71            n_f,
72            n_plus_1,
73            half_n,
74            inv_n,
75            cst1,
76            cst2,
77        }
78    }
79
80    #[doc = svgbobdoc::transform!(
81    /// The function calculates the core values for updating an ellipsoid with either a parallel-cut or
82    /// a deep-cut.
83    ///
84    /// Arguments:
85    ///
86    /// * `beta0`: The parameter `beta0` represents the semi-minor axis of the ellipsoid before the cut. It is
87    ///            a floating-point number.
88    /// * `beta1`: The parameter `beta1` represents the length of the semi-minor axis of the ellipsoid.
89    /// * `tsq`: tsq is a reference to a f64 value, which represents the square of the semi-major axis
90    ///            of the ellipsoid.
91    ///
92    /// ```svgbob
93    ///      _.-'''''''-._
94    ///    ,'     |       `.
95    ///   /  |    |         \
96    ///  .   |    |          .
97    ///  |   |    |          |
98    ///  |   |    |.         |
99    ///  |   |    |          |
100    ///  :\  |    |         /:
101    ///  | `._    |      _.' |
102    ///  |   |'-.......-'    |
103    ///  |   |    |          |
104    /// "-τ" "-β" "-β"      +τ
105    ///        1    0
106    /// 
107    ///      β  + β                            
108    ///       0    1                           
109    ///  β = ───────                           
110    ///         2                              
111    ///                                        
112    ///      1   ⎛ 2          ⎞        2       
113    ///  h = ─ ⋅ ⎜τ  + β  ⋅ β ⎟ + n ⋅ β        
114    ///      2   ⎝      0    1⎠                
115    ///             _____________________      
116    ///            ╱ 2                  2      
117    ///  k = h + ╲╱ h  - (n + 1) ⋅ η ⋅ β       
118    ///                                        
119    ///        1     η                         
120    ///  σ = ───── = ─                         
121    ///      μ + 1   k                         
122    ///                                        
123    ///  1     η                               
124    ///  ─ = ─────                             
125    ///  μ   k - η                             
126    ///                                        
127    ///  ϱ = β ⋅ σ                            
128    ///                                        
129    ///       2    2   1   ⎛ 2              ⎞  
130    ///  δ ⋅ τ  = τ  + ─ ⋅ ⎜β  ⋅ σ - β  ⋅ β ⎟  
131    ///                μ   ⎝          0    1⎠   
132    /// ```
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use approx_eq::assert_approx_eq;
138    /// use ellalgo_rs::ell_calc::EllCalcCore;
139    ///
140    /// let ell_calc_core = EllCalcCore::new(4.0);
141    /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_bias_cut_fast(1.0, 2.0, 4.0, 2.0, 12.0);
142    /// assert_approx_eq!(rho, 1.2);
143    /// assert_approx_eq!(sigma, 0.8);
144    /// assert_approx_eq!(delta, 0.8);
145    /// ```
146    )]
147    pub fn calc_parallel_bias_cut_fast(
148        &self,
149        beta0: f64,
150        beta1: f64,
151        tsq: f64,
152        b0b1: f64,
153        eta: f64,
154    ) -> (f64, f64, f64) {
155        let bavg = (beta0 + beta1) * 0.5;
156        let bavgsq = bavg * bavg;
157        let h = (tsq + b0b1) * 0.5 + self.n_f * bavgsq;
158        let k = h + (h * h - eta * self.n_plus_1 * bavgsq).sqrt();
159        let inv_mu_plus_1 = eta / k;
160        let inv_mu = eta / (k - eta);
161        let rho = bavg * inv_mu_plus_1;
162        let sigma = inv_mu_plus_1;
163        let delta = (tsq + inv_mu * (bavgsq * inv_mu_plus_1 - b0b1)) / tsq;
164
165        (rho, sigma, delta)
166    }
167
168    #[doc = svgbobdoc::transform!(
169    /// The function calculates the core values for updating an ellipsoid with either a parallel-cut or
170    /// a deep-cut.
171    ///
172    /// Arguments:
173    ///
174    /// * `beta0`: The parameter `beta0` represents the semi-minor axis of the ellipsoid before the cut. It is
175    ///            a floating-point number.
176    /// * `beta1`: The parameter `beta1` represents the length of the semi-minor axis of the ellipsoid.
177    /// * `tsq`: tsq is a reference to a f64 value, which represents the square of the semi-major axis
178    ///            of the ellipsoid.
179    ///
180    /// ```svgbob
181    ///      _.-'''''''-._
182    ///    ,'     |       `.
183    ///   /  |    |         \
184    ///  .   |    |          .
185    ///  |   |    |          |
186    ///  |   |    |.         |
187    ///  |   |    |          |
188    ///  :\  |    |         /:
189    ///  | `._    |      _.' |
190    ///  |   |'-.......-'    |
191    ///  |   |    |          |
192    /// "-τ" "-β" "-β"      +τ
193    ///        1    0
194    /// 
195    ///       2                            
196    ///  η = τ  + n ⋅ β  ⋅ β               
197    ///                0    1                
198    ///      β  + β                            
199    ///       0    1                           
200    ///  β = ───────                           
201    ///         2                              
202    ///                                        
203    ///      1   ⎛ 2          ⎞        2       
204    ///  h = ─ ⋅ ⎜τ  + β  ⋅ β ⎟ + n ⋅ β        
205    ///      2   ⎝      0    1⎠                
206    ///             _____________________      
207    ///            ╱ 2                  2      
208    ///  k = h + ╲╱ h  - (n + 1) ⋅ η ⋅ β       
209    ///                                        
210    ///        1     η                         
211    ///  σ = ───── = ─                         
212    ///      μ + 1   k                         
213    ///                                        
214    ///  1     η                               
215    ///  ─ = ─────                             
216    ///  μ   k - η                             
217    ///                                        
218    ///  ϱ = β ⋅ σ                            
219    ///                                        
220    ///       2    2   1   ⎛ 2              ⎞  
221    ///  δ ⋅ τ  = τ  + ─ ⋅ ⎜β  ⋅ σ - β  ⋅ β ⎟  
222    ///                μ   ⎝          0    1⎠   
223    /// ```
224    ///
225    /// # Examples
226    ///
227    /// ```rust
228    /// use approx_eq::assert_approx_eq;
229    /// use ellalgo_rs::ell_calc::EllCalcCore;
230    ///
231    /// let ell_calc_core = EllCalcCore::new(4.0);
232    /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_bias_cut(1.0, 2.0, 4.0);
233    /// assert_approx_eq!(rho, 1.2);
234    /// assert_approx_eq!(sigma, 0.8);
235    /// assert_approx_eq!(delta, 0.8);
236    /// ```
237    )]
238    #[inline]
239    pub fn calc_parallel_bias_cut(&self, beta0: f64, beta1: f64, tsq: f64) -> (f64, f64, f64) {
240        let b0b1 = beta0 * beta1;
241        let eta = tsq + self.n_f * b0b1;
242        self.calc_parallel_bias_cut_fast(beta0, beta1, tsq, b0b1, eta)
243    }
244
245    #[doc = svgbobdoc::transform!(
246    /// The function calculates the core values for updating an ellipsoid with the parallel-cut method.
247    ///
248    /// Arguments:
249    ///
250    /// * `beta1`: The parameter `beta1` represents the semi-minor axis of the ellipsoid. It is a floating-point
251    ///            number.
252    /// * `tsq`: The parameter `tsq` represents the square of the gamma semi-axis length of the ellipsoid.
253    ///
254    /// ```svgbob
255    ///      _.-'''''''-._
256    ///    ,'      |      `.
257    ///   /  |     |        \
258    ///  .   |     |         .
259    ///  |   |               |
260    ///  |   |     .         |
261    ///  |   |               |
262    ///  :\  |     |        /:
263    ///  | `._     |     _.' |
264    ///  |   |'-.......-'    |
265    ///  |   |     |         |
266    /// "-τ" "-β"  0        +τ
267    ///        1
268    ///
269    ///   2    2    2
270    ///  α  = β  / τ
271    ///
272    ///      n    2
273    ///  h = ─ ⋅ α
274    ///      2
275    ///             ___________
276    ///            ╱ 2        2
277    ///  r = h + ╲╱ h  + 1 - α
278    ///
279    ///        β
280    ///  ϱ = ─────
281    ///      r + 1
282    ///
283    ///        2
284    ///  σ = ─────
285    ///      r + 1
286    ///
287    ///          r
288    ///  δ = ─────────
289    ///      r - 1 / n
290    /// ```
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// use approx_eq::assert_approx_eq;
296    /// use ellalgo_rs::ell_calc::EllCalcCore;
297    ///
298    /// let ell_calc_core = EllCalcCore::new(4.0);
299    /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_central_cut(1.0, 4.0);
300    /// assert_approx_eq!(rho, 0.4);
301    /// assert_approx_eq!(sigma, 0.8);
302    /// assert_approx_eq!(delta, 1.2);
303    /// ```
304    )]
305    pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: f64) -> (f64, f64, f64) {
306        let b1sq = beta1 * beta1;
307        let a1sq = b1sq / tsq;
308        let h = self.half_n * a1sq;
309        let r = h + (1.0 - a1sq + h * h).sqrt();
310        let r_plus_1 = r + 1.0;
311        let rho = beta1 / r_plus_1;
312        let sigma = 2.0 / r_plus_1;
313        let delta = r / (r - self.inv_n);
314
315        (rho, sigma, delta)
316    }
317
318    #[doc = svgbobdoc::transform!(
319    /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
320    ///
321    /// Arguments:
322    ///
323    /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
324    ///          the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
325    ///          number.
326    /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
327    ///          quickly the system responds to changes.
328    /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
329    ///          ellipsoid is being updated or modified.
330    ///
331    /// ```svgbob
332    ///       _.-'''''''-._
333    ///     ,'    |        `.
334    ///    /      |          \
335    ///   .       |           .
336    ///   |       |           |
337    ///   |       | .         |
338    ///   |       |           |
339    ///   :\      |          /:
340    ///   | `._   |       _.' |
341    ///   |    '-.......-'    |
342    ///   |       |           |
343    ///  "-τ"     "-β"       +τ
344    ///
345    ///         η
346    ///   ϱ = ─────
347    ///       n + 1
348    ///
349    ///       2 ⋅ ϱ
350    ///   σ = ─────
351    ///       "τ + β"
352    ///
353    ///          2       2    2
354    ///         n       τ  - β
355    ///   δ = ────── ⋅  ───────
356    ///        2           2
357    ///       n  - 1      τ
358    /// ```
359    ///
360    /// # Example
361    ///
362    /// ```
363    /// use approx_eq::assert_approx_eq;
364    /// use ellalgo_rs::ell_calc::EllCalcCore;
365    ///
366    /// let ell_calc_core = EllCalcCore::new(4.0);
367    /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut_fast(1.0, 2.0, 6.0);
368    /// assert_approx_eq!(rho, 1.2);
369    /// assert_approx_eq!(sigma, 0.8);
370    /// assert_approx_eq!(delta, 0.8);
371    /// ```
372    )]
373    pub fn calc_bias_cut_fast(&self, beta: f64, tau: f64, eta: f64) -> (f64, f64, f64) {
374        let rho = eta / self.n_plus_1;
375        let sigma = 2.0 * rho / (tau + beta);
376        let alpha = beta / tau;
377        let delta = self.cst1 * (1.0 - alpha * alpha);
378        (rho, sigma, delta)
379    }
380
381    #[doc = svgbobdoc::transform!(
382    /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
383    ///
384    /// Arguments:
385    ///
386    /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
387    ///          the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
388    ///          number.
389    /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
390    ///          quickly the system responds to changes.
391    /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
392    ///          ellipsoid is being updated or modified.
393    ///
394    /// ```svgbob
395    ///       _.-'''''''-._
396    ///     ,'    |        `.
397    ///    /      |          \
398    ///   .       |           .
399    ///   |       |           |
400    ///   |       | .         |
401    ///   |       |           |
402    ///   :\      |          /:
403    ///   | `._   |       _.' |
404    ///   |    '-.......-'    |
405    ///   |       |           |
406    ///  "-τ"     "-β"       +τ
407    ///
408    ///   η = τ + n ⋅ β
409    ///   
410    ///         η
411    ///   ϱ = ─────
412    ///       n + 1
413    ///
414    ///       2 ⋅ ϱ
415    ///   σ = ─────
416    ///       "τ + β"
417    ///
418    ///          2       2    2
419    ///         n       τ  - β
420    ///   δ = ────── ⋅  ───────
421    ///        2           2
422    ///       n  - 1      τ
423    /// ```
424    ///
425    /// # Example
426    ///
427    /// ```
428    /// use approx_eq::assert_approx_eq;
429    /// use ellalgo_rs::ell_calc::EllCalcCore;
430    ///
431    /// let ell_calc_core = EllCalcCore::new(4.0);
432    /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut(1.0, 2.0);
433    /// assert_approx_eq!(rho, 1.2);
434    /// assert_approx_eq!(sigma, 0.8);
435    /// assert_approx_eq!(delta, 0.8);
436    /// ```
437    )]
438    #[inline]
439    pub fn calc_bias_cut(&self, beta: f64, tau: f64) -> (f64, f64, f64) {
440        let eta = tau + self.n_f * beta;
441        self.calc_bias_cut_fast(beta, tau, eta)
442    }
443
444    #[doc = svgbobdoc::transform!(
445    /// The `calc_central_cut_core` function calculates the core values needed for updating an ellipsoid with the
446    /// central-cut.
447    ///
448    /// Arguments:
449    ///
450    /// * `tsq`: The parameter `tsq` represents the square of the time taken to update the ellipsoid with
451    ///          the central-cut.
452    ///
453    /// ```svgbob
454    ///       _.-'''''''-._
455    ///     ,'      |      `.
456    ///    /        |        \
457    ///   .         |         .
458    ///   |                   |
459    ///   |         .         |
460    ///   |                   |
461    ///   :\        |        /:
462    ///   | `._     |     _.' |
463    ///   |    '-.......-'    |
464    ///   |         |         |
465    ///  "-τ"       0        +τ
466    ///
467    ///         2
468    ///   σ = ─────
469    ///       n + 1
470    ///
471    ///         τ
472    ///   ϱ = ─────
473    ///       n + 1
474    ///
475    ///          2
476    ///         n
477    ///   δ = ──────
478    ///        2
479    ///       n  - 1
480    /// ```
481    ///
482    /// # Example
483    ///
484    /// ```rust
485    /// use approx_eq::assert_approx_eq;
486    /// use ellalgo_rs::ell_calc::EllCalcCore;
487    /// let ell_calc_core = EllCalcCore::new(4.0);
488    /// let (rho, sigma, delta) = ell_calc_core.calc_central_cut(4.0);
489    /// assert_approx_eq!(rho, 0.4);
490    /// assert_approx_eq!(sigma, 0.4);
491    /// assert_approx_eq!(delta, 16.0/15.0);
492    /// ```
493    )]
494    pub fn calc_central_cut(&self, tsq: f64) -> (f64, f64, f64) {
495        // self.mu = self.half_n_minus_1;
496        let sigma = self.cst2;
497        let rho = tsq.sqrt() / self.n_plus_1;
498        let delta = self.cst1;
499        (rho, sigma, delta)
500    }
501}
502
503/// The `EllCalc` struct represents an ellipsoid search space in Rust.
504///
505///  EllCalc = {x | (x - xc)^T mq^-1 (x - xc) \le \kappa}
506///
507/// Properties:
508///
509/// * `n_f`: The `n_f` property is a floating-point number that represents the dimensionality of the
510///          search space. It indicates the number of variables or dimensions in the ellipsoid search space.
511/// * `helper`: The `helper` property is of type `EllCalcCore` and is used to perform
512///          calculations related to the ellipsoid search space. It is a separate struct that contains the
513///          necessary methods and data for these calculations.
514/// * `use_parallel_cut`: A boolean flag indicating whether to use parallel cut or not.
515#[derive(Debug, Clone)]
516pub struct EllCalc {
517    n_f: f64,
518    pub helper: EllCalcCore,
519    pub use_parallel_cut: bool,
520}
521
522impl EllCalc {
523    /// The `new` function constructs a new [`EllCalc`] object with a given value for `n_f` and sets the
524    /// `use_parallel_cut` flag to `true`.
525    ///
526    /// Arguments:
527    ///
528    /// * `n_f`: The parameter `n_f` is a floating-point number that is used to initialize the `EllCalc`
529    ///          struct.
530    ///
531    /// Returns:
532    ///
533    /// The `new` function is returning an instance of the `EllCalc` struct.
534    ///
535    /// # Examples:
536    ///
537    /// ```rust
538    /// use ellalgo_rs::ell_calc::EllCalc;
539    /// let ell_calc = EllCalc::new(4);
540    /// assert!(ell_calc.use_parallel_cut);
541    /// ```
542    pub fn new(n: usize) -> EllCalc {
543        let n_f = n as f64;
544        let helper = EllCalcCore::new(n_f);
545
546        EllCalc {
547            n_f,
548            helper,
549            use_parallel_cut: true,
550        }
551    }
552
553    // pub fn update_cut(&mut self, beta: f64) -> CutStatus { self.calc_bias_cut(beta) }
554
555    /// The function calculates the updating of an ellipsoid with a single or parallel-cut.
556    ///
557    /// Arguments:
558    ///
559    /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
560    ///           `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
561    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
562    pub fn calc_single_or_parallel_bias_cut(
563        &self,
564        beta: &(f64, Option<f64>),
565        tsq: f64,
566    ) -> (CutStatus, (f64, f64, f64)) {
567        let (beta0, beta1_opt) = *beta;
568        if let Some(beta1) = beta1_opt {
569            self.calc_parallel_bias_cut(beta0, beta1, tsq)
570        } else {
571            self.calc_bias_cut(beta0, tsq)
572        }
573    }
574
575    /// The function calculates the updating of an ellipsoid with a single or parallel-cut (one of them is central-cut).
576    ///
577    /// Arguments:
578    ///
579    /// * `beta`: The `beta` parameter is a tuple containing two values: `f64` and `Option<f64>`.
580    ///           The first value, denoted as `_b0`, is of type `f64`. The second value, `beta1_opt`, is of type `Option<f64>`.
581    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
582    pub fn calc_single_or_parallel_central_cut(
583        &self,
584        beta: &(f64, Option<f64>),
585        tsq: f64,
586    ) -> (CutStatus, (f64, f64, f64)) {
587        let (_b0, beta1_opt) = *beta;
588        if let Some(beta1) = beta1_opt {
589            self.calc_parallel_central_cut(beta1, tsq)
590        } else {
591            self.calc_central_cut(tsq)
592        }
593    }
594
595    /// The function calculates the updating of an ellipsoid with a single or parallel-cut (discrete version).
596    ///
597    /// Arguments:
598    ///
599    /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
600    ///           `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
601    /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
602    pub fn calc_single_or_parallel_q(
603        &self,
604        beta: &(f64, Option<f64>),
605        tsq: f64,
606    ) -> (CutStatus, (f64, f64, f64)) {
607        let (beta0, beta1_opt) = *beta;
608        if let Some(beta1) = beta1_opt {
609            self.calc_parallel_q(beta0, beta1, tsq)
610        } else {
611            self.calc_bias_cut_q(beta0, tsq)
612        }
613    }
614
615    /// Parallel Deep Cut
616    ///
617    /// # Examples:
618    ///
619    /// ```rust
620    /// use approx_eq::assert_approx_eq;
621    /// use ellalgo_rs::ell_calc::EllCalc;
622    /// use ellalgo_rs::cutting_plane::CutStatus;
623    ///
624    /// let ell_calc = EllCalc::new(4);
625    /// let (status, _result) = ell_calc.calc_parallel_bias_cut(0.07, 0.03, 0.01);
626    /// assert_eq!(status, CutStatus::NoSoln);
627    ///
628    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.0, 0.05, 0.01);
629    /// assert_eq!(status, CutStatus::Success);
630    /// assert_approx_eq!(sigma, 0.8);
631    /// assert_approx_eq!(rho, 0.02);
632    /// assert_approx_eq!(delta, 1.2);
633    ///
634    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.05, 0.11, 0.01);
635    /// assert_eq!(status, CutStatus::Success);
636    /// assert_approx_eq!(sigma, 0.8);
637    /// assert_approx_eq!(rho, 0.06);
638    /// assert_approx_eq!(delta, 0.8);
639    ///
640    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.01, 0.04, 0.01);
641    /// assert_eq!(status, CutStatus::Success);
642    /// assert_approx_eq!(sigma, 0.928);
643    /// assert_approx_eq!(rho, 0.0232);
644    /// assert_approx_eq!(delta, 1.232);
645    /// ```
646    pub fn calc_parallel_bias_cut(
647        &self,
648        beta0: f64,
649        beta1: f64,
650        tsq: f64,
651    ) -> (CutStatus, (f64, f64, f64)) {
652        if beta1 < beta0 {
653            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
654        }
655
656        let b1sqn = beta1 * (beta1 / tsq);
657        let t1n = 1.0 - b1sqn;
658        if t1n < 0.0 || !self.use_parallel_cut {
659            return self.calc_bias_cut(beta0, tsq);
660        }
661
662        (
663            CutStatus::Success,
664            self.helper.calc_parallel_bias_cut(beta0, beta1, tsq),
665        )
666    }
667
668    /// Discrete Parallel Deep Cut
669    ///
670    /// # Examples:
671    ///
672    /// ```rust
673    /// use approx_eq::assert_approx_eq;
674    /// use ellalgo_rs::ell_calc::EllCalc;
675    /// use ellalgo_rs::cutting_plane::CutStatus;
676    ///
677    /// let ell_calc = EllCalc::new(4);
678    /// let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, 0.01);
679    /// assert_eq!(status, CutStatus::NoEffect);
680    ///
681    /// let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, 0.01);
682    /// assert_eq!(status, CutStatus::NoEffect);
683    /// ```
684    pub fn calc_parallel_q(
685        &self,
686        beta0: f64,
687        beta1: f64,
688        tsq: f64,
689    ) -> (CutStatus, (f64, f64, f64)) {
690        if beta1 < beta0 {
691            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
692        }
693
694        if (beta1 > 0.0) && beta1 * beta1 >= tsq || !self.use_parallel_cut {
695            return self.calc_bias_cut_q(beta0, tsq);
696        }
697
698        let b0b1 = beta0 * beta1;
699        let eta = tsq + self.n_f * b0b1;
700        if eta <= 0.0 {
701            return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
702        }
703
704        (
705            CutStatus::Success,
706            self.helper
707                .calc_parallel_bias_cut_fast(beta0, beta1, tsq, b0b1, eta),
708        )
709    }
710
711    /// Parallel Central Cut
712    ///
713    /// # Examples:
714    ///
715    /// ```rust
716    /// use approx_eq::assert_approx_eq;
717    /// use ellalgo_rs::ell_calc::EllCalc;
718    /// use ellalgo_rs::cutting_plane::CutStatus;
719    ///
720    /// let ell_calc = EllCalc::new(4);
721    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, 0.01);
722    /// assert_eq!(status, CutStatus::Success);
723    /// assert_approx_eq!(sigma, 0.4);
724    /// assert_approx_eq!(rho, 0.02);
725    /// assert_approx_eq!(delta, 16.0 / 15.0);
726    ///
727    /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, 0.01);
728    /// assert_eq!(status, CutStatus::Success);
729    /// assert_approx_eq!(sigma, 0.8);
730    /// assert_approx_eq!(rho, 0.02);
731    /// assert_approx_eq!(delta, 1.2);
732    /// ```
733    pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
734        if beta1 < 0.0 {
735            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no solution
736        }
737        if tsq <= beta1 * beta1 || !self.use_parallel_cut {
738            return self.calc_central_cut(tsq);
739        }
740        (
741            CutStatus::Success,
742            self.helper.calc_parallel_central_cut(beta1, tsq),
743        )
744    }
745
746    /// Deep Cut
747    ///
748    /// # Examples:
749    ///
750    /// ```rust
751    /// use approx_eq::assert_approx_eq;
752    /// use ellalgo_rs::ell_calc::EllCalc;
753    /// use ellalgo_rs::cutting_plane::CutStatus;
754    ///
755    /// let ell_calc = EllCalc::new(4);
756    /// let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
757    /// assert_eq!(status, CutStatus::NoSoln);
758    /// let (status, _result) = ell_calc.calc_bias_cut(0.0, 0.01);
759    /// assert_eq!(status, CutStatus::Success);
760    ///
761    /// let (status, (rho, sigma, delta)) = ell_calc.calc_bias_cut(0.05, 0.01);
762    /// assert_eq!(status, CutStatus::Success);
763    /// assert_approx_eq!(sigma, 0.8);
764    /// assert_approx_eq!(rho, 0.06);
765    /// assert_approx_eq!(delta, 0.8);
766    /// ```
767    pub fn calc_bias_cut(&self, beta: f64, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
768        if tsq < beta * beta {
769            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
770        }
771
772        let tau = tsq.sqrt();
773        (CutStatus::Success, self.helper.calc_bias_cut(beta, tau))
774    }
775
776    /// Discrete Deep Cut
777    ///
778    /// # Examples:
779    ///
780    /// ```rust
781    /// use approx_eq::assert_approx_eq;
782    /// use ellalgo_rs::ell_calc::EllCalc;
783    /// use ellalgo_rs::cutting_plane::CutStatus;
784    ///
785    /// let ell_calc = EllCalc::new(4);
786    /// let (status, _result) = ell_calc.calc_bias_cut_q(-0.05, 0.01);
787    /// assert_eq!(status, CutStatus::NoEffect);
788    /// ```
789    pub fn calc_bias_cut_q(&self, beta: f64, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
790        let tau = tsq.sqrt();
791
792        if tau < beta {
793            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
794        }
795
796        let eta = tau + self.n_f * beta;
797        if eta < 0.0 {
798            return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
799        }
800
801        (
802            CutStatus::Success,
803            self.helper.calc_bias_cut_fast(beta, tau, eta),
804        )
805    }
806
807    /// Central Cut
808    ///
809    /// # Examples:
810    ///
811    /// ```rust
812    /// use approx_eq::assert_approx_eq;
813    /// use ellalgo_rs::ell_calc::EllCalc;
814    /// use ellalgo_rs::cutting_plane::CutStatus;
815    ///
816    /// let ell_calc = EllCalc::new(4);
817    /// let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(0.01);
818    ///
819    /// assert_eq!(status, CutStatus::Success);
820    /// assert_approx_eq!(sigma, 0.4);
821    /// assert_approx_eq!(rho, 0.02);
822    /// assert_approx_eq!(delta, 16.0 / 15.0);
823    /// ```
824    #[inline]
825    pub fn calc_central_cut(&self, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
826        // self.mu = self.half_n_minus_1;
827        (CutStatus::Success, self.helper.calc_central_cut(tsq))
828    }
829}
830
831// pub trait UpdateByCutChoice {
832//     fn update_by(self, ell: &mut EllCalc) -> CutStatus;
833// }
834#[cfg(test)]
835mod tests {
836    use super::*;
837    use approx_eq::assert_approx_eq;
838
839    #[test]
840    pub fn test_construct() {
841        let helper = EllCalcCore::new(4.0);
842        assert_eq!(helper.n_f, 4.0);
843        assert_eq!(helper.half_n, 2.0);
844        assert_approx_eq!(helper.cst1, 16.0 / 15.0);
845        assert_approx_eq!(helper.cst2, 0.4);
846    }
847
848    #[test]
849    pub fn test_calc_parallel_bias_cut_fast() {
850        let ell_calc_core = EllCalcCore::new(4.0);
851        let (rho, sigma, delta) =
852            ell_calc_core.calc_parallel_bias_cut_fast(1.0, 2.0, 4.0, 2.0, 12.0);
853        assert_approx_eq!(rho, 1.2);
854        assert_approx_eq!(sigma, 0.8);
855        assert_approx_eq!(delta, 0.8);
856    }
857
858    #[test]
859    pub fn test_calc_bias_cut_fast() {
860        let ell_calc_core = EllCalcCore::new(4.0);
861        let (rho, sigma, delta) = ell_calc_core.calc_bias_cut_fast(1.0, 2.0, 6.0);
862        assert_approx_eq!(rho, 1.2);
863        assert_approx_eq!(sigma, 0.8);
864        assert_approx_eq!(delta, 0.8);
865    }
866
867    #[test]
868    pub fn test_calc_central_cut() {
869        let ell_calc = EllCalc::new(4);
870        // ell_calc.tsq = 0.01;
871        let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(0.01);
872        assert_eq!(status, CutStatus::Success);
873        assert_approx_eq!(sigma, 0.4);
874        assert_approx_eq!(rho, 0.02);
875        assert_approx_eq!(delta, 16.0 / 15.0);
876    }
877
878    #[test]
879    pub fn test_calc_bias_cut() {
880        let ell_calc = EllCalc::new(4);
881        // ell_calc.tsq = 0.01;
882        let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
883        assert_eq!(status, CutStatus::NoSoln);
884        let (status, _result) = ell_calc.calc_bias_cut(0.0, 0.01);
885        assert_eq!(status, CutStatus::Success);
886        let (status, _result) = ell_calc.calc_bias_cut_q(-0.05, 0.01);
887        assert_eq!(status, CutStatus::NoEffect);
888
889        // ell_calc.tsq = 0.01;
890        let (status, (rho, sigma, delta)) = ell_calc.calc_bias_cut(0.05, 0.01);
891        assert_eq!(status, CutStatus::Success);
892        assert_approx_eq!(sigma, 0.8);
893        assert_approx_eq!(rho, 0.06);
894        assert_approx_eq!(delta, 0.8);
895    }
896
897    #[test]
898    pub fn test_calc_parallel_central_cut() {
899        let ell_calc = EllCalc::new(4);
900        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, 0.01);
901        assert_eq!(status, CutStatus::Success);
902        assert_approx_eq!(sigma, 0.4);
903        assert_approx_eq!(rho, 0.02);
904        assert_approx_eq!(delta, 16.0 / 15.0);
905
906        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, 0.01);
907        assert_eq!(status, CutStatus::Success);
908        assert_approx_eq!(sigma, 0.8);
909        assert_approx_eq!(rho, 0.02);
910        assert_approx_eq!(delta, 1.2);
911    }
912
913    #[test]
914    pub fn test_calc_parallel() {
915        let ell_calc = EllCalc::new(4);
916        // ell_calc.tsq = 0.01;
917        let (status, _result) = ell_calc.calc_parallel_bias_cut(0.07, 0.03, 0.01);
918        assert_eq!(status, CutStatus::NoSoln);
919
920        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.0, 0.05, 0.01);
921        assert_eq!(status, CutStatus::Success);
922        assert_approx_eq!(sigma, 0.8);
923        assert_approx_eq!(rho, 0.02);
924        assert_approx_eq!(delta, 1.2);
925
926        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.05, 0.11, 0.01);
927        assert_eq!(status, CutStatus::Success);
928        assert_approx_eq!(sigma, 0.8);
929        assert_approx_eq!(rho, 0.06);
930        assert_approx_eq!(delta, 0.8);
931
932        let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, 0.01);
933        assert_eq!(status, CutStatus::NoEffect);
934
935        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.01, 0.04, 0.01);
936        assert_eq!(status, CutStatus::Success);
937        assert_approx_eq!(sigma, 0.928);
938        assert_approx_eq!(rho, 0.0232);
939        assert_approx_eq!(delta, 1.232);
940
941        let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, 0.01);
942        assert_eq!(status, CutStatus::NoEffect);
943    }
944
945    #[test]
946    fn test_construct2() {
947        let ell_calc = EllCalc::new(4);
948        assert!(ell_calc.use_parallel_cut);
949        assert_eq!(ell_calc.n_f, 4.0);
950    }
951
952    #[test]
953    fn test_calc_central_cut2() {
954        let ell_calc = EllCalc::new(4);
955        let (status, result) =
956            ell_calc.calc_single_or_parallel_central_cut(&(0.0, Some(0.05)), 0.01);
957        assert_eq!(status, CutStatus::Success);
958        let (rho, sigma, delta) = result;
959        assert_approx_eq!(sigma, 0.8);
960        assert_approx_eq!(rho, 0.02);
961        assert_approx_eq!(delta, 1.2);
962    }
963
964    #[test]
965    fn test_calc_bias_cut2() {
966        let ell_calc = EllCalc::new(4);
967        let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
968        assert_eq!(status, CutStatus::NoSoln);
969        let (status, _result) = ell_calc.calc_bias_cut(0.01, 0.01);
970        assert_eq!(status, CutStatus::Success);
971
972        let (status, result) = ell_calc.calc_bias_cut(0.05, 0.01);
973        assert_eq!(status, CutStatus::Success);
974        let (rho, sigma, delta) = result;
975        assert_approx_eq!(rho, 0.06);
976        assert_approx_eq!(sigma, 0.8);
977        assert_approx_eq!(delta, 0.8);
978    }
979
980    #[test]
981    fn test_calc_parallel_central_cut2() {
982        let ell_calc = EllCalc::new(4);
983        let (status, result) =
984            ell_calc.calc_single_or_parallel_central_cut(&(0.0, Some(0.05)), 0.01);
985        assert_eq!(status, CutStatus::Success);
986        let (rho, sigma, delta) = result;
987        assert_approx_eq!(rho, 0.02);
988        assert_approx_eq!(sigma, 0.8);
989        assert_approx_eq!(delta, 1.2);
990    }
991
992    #[test]
993    fn test_calc_parallel2() {
994        let ell_calc = EllCalc::new(4);
995        let (status, _result) = ell_calc.calc_parallel_bias_cut(0.07, 0.03, 0.01);
996        assert_eq!(status, CutStatus::NoSoln);
997        let (status, result) = ell_calc.calc_parallel_bias_cut(0.0, 0.05, 0.01);
998        assert_eq!(status, CutStatus::Success);
999        let (rho, sigma, delta) = result;
1000        assert_approx_eq!(rho, 0.02);
1001        assert_approx_eq!(sigma, 0.8);
1002        assert_approx_eq!(delta, 1.2);
1003        let (status, result) = ell_calc.calc_parallel_bias_cut(0.05, 0.11, 0.01);
1004        assert_eq!(status, CutStatus::Success);
1005        let (rho, sigma, delta) = result;
1006        assert_approx_eq!(rho, 0.06);
1007        assert_approx_eq!(sigma, 0.8);
1008        assert_approx_eq!(delta, 0.8);
1009        let (status, result) = ell_calc.calc_parallel_bias_cut(0.01, 0.04, 0.01);
1010        assert_eq!(status, CutStatus::Success);
1011        let (rho, sigma, delta) = result;
1012        assert_approx_eq!(rho, 0.0232);
1013        assert_approx_eq!(sigma, 0.928);
1014        assert_approx_eq!(delta, 1.232);
1015    }
1016
1017    #[test]
1018    fn test_calc_bias_cut_q2() {
1019        let ell_calc_q = EllCalc::new(4);
1020        let (status, _result) = ell_calc_q.calc_bias_cut_q(0.11, 0.01);
1021        assert_eq!(status, CutStatus::NoSoln);
1022        let (status, _result) = ell_calc_q.calc_bias_cut_q(0.01, 0.01);
1023        assert_eq!(status, CutStatus::Success);
1024        let (status, _result) = ell_calc_q.calc_bias_cut_q(-0.05, 0.01);
1025        assert_eq!(status, CutStatus::NoEffect);
1026        let (status, result) = ell_calc_q.calc_bias_cut_q(0.05, 0.01);
1027        assert_eq!(status, CutStatus::Success);
1028        let (rho, sigma, delta) = result;
1029        assert_approx_eq!(rho, 0.06);
1030        assert_approx_eq!(sigma, 0.8);
1031        assert_approx_eq!(delta, 0.8);
1032    }
1033
1034    #[test]
1035    fn test_calc_parallel_q2() {
1036        let ell_calc = EllCalc::new(4);
1037        let (status, _result) = ell_calc.calc_parallel_q(0.07, 0.03, 0.01);
1038        assert_eq!(status, CutStatus::NoSoln);
1039        let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, 0.01);
1040        assert_eq!(status, CutStatus::NoEffect);
1041        let (status, result) = ell_calc.calc_parallel_q(0.0, 0.05, 0.01);
1042        assert_eq!(status, CutStatus::Success);
1043        let (rho, sigma, delta) = result;
1044        assert_approx_eq!(rho, 0.02);
1045        assert_approx_eq!(sigma, 0.8);
1046        assert_approx_eq!(delta, 1.2);
1047        let (status, result) = ell_calc.calc_parallel_q(0.05, 0.11, 0.01);
1048        assert_eq!(status, CutStatus::Success);
1049        let (rho, sigma, delta) = result;
1050        assert_approx_eq!(rho, 0.06);
1051        assert_approx_eq!(sigma, 0.8);
1052        assert_approx_eq!(delta, 0.8);
1053        let (status, result) = ell_calc.calc_parallel_q(0.01, 0.04, 0.01);
1054        assert_eq!(status, CutStatus::Success);
1055        let (rho, sigma, delta) = result;
1056        assert_approx_eq!(rho, 0.0232);
1057        assert_approx_eq!(sigma, 0.928);
1058        assert_approx_eq!(delta, 1.232);
1059    }
1060}