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        let b1sqn = beta1 * (beta1 / tsq);
738        let t1n = 1.0 - b1sqn;
739        if t1n < 0.0 || !self.use_parallel_cut {
740            return self.calc_central_cut(tsq);
741        }
742        (
743            CutStatus::Success,
744            self.helper.calc_parallel_central_cut(beta1, tsq),
745        )
746    }
747
748    /// Deep Cut
749    ///
750    /// # Examples:
751    ///
752    /// ```rust
753    /// use approx_eq::assert_approx_eq;
754    /// use ellalgo_rs::ell_calc::EllCalc;
755    /// use ellalgo_rs::cutting_plane::CutStatus;
756    ///
757    /// let ell_calc = EllCalc::new(4);
758    /// let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
759    /// assert_eq!(status, CutStatus::NoSoln);
760    /// let (status, _result) = ell_calc.calc_bias_cut(0.0, 0.01);
761    /// assert_eq!(status, CutStatus::Success);
762    ///
763    /// let (status, (rho, sigma, delta)) = ell_calc.calc_bias_cut(0.05, 0.01);
764    /// assert_eq!(status, CutStatus::Success);
765    /// assert_approx_eq!(sigma, 0.8);
766    /// assert_approx_eq!(rho, 0.06);
767    /// assert_approx_eq!(delta, 0.8);
768    /// ```
769    pub fn calc_bias_cut(&self, beta: f64, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
770        if tsq < beta * beta {
771            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
772        }
773
774        let tau = tsq.sqrt();
775        (CutStatus::Success, self.helper.calc_bias_cut(beta, tau))
776    }
777
778    /// Discrete Deep Cut
779    ///
780    /// # Examples:
781    ///
782    /// ```rust
783    /// use approx_eq::assert_approx_eq;
784    /// use ellalgo_rs::ell_calc::EllCalc;
785    /// use ellalgo_rs::cutting_plane::CutStatus;
786    ///
787    /// let ell_calc = EllCalc::new(4);
788    /// let (status, _result) = ell_calc.calc_bias_cut_q(-0.05, 0.01);
789    /// assert_eq!(status, CutStatus::NoEffect);
790    /// ```
791    pub fn calc_bias_cut_q(&self, beta: f64, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
792        let tau = tsq.sqrt();
793
794        if tau < beta {
795            return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
796        }
797
798        let eta = tau + self.n_f * beta;
799        if eta < 0.0 {
800            return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
801        }
802
803        (
804            CutStatus::Success,
805            self.helper.calc_bias_cut_fast(beta, tau, eta),
806        )
807    }
808
809    /// Central Cut
810    ///
811    /// # Examples:
812    ///
813    /// ```rust
814    /// use approx_eq::assert_approx_eq;
815    /// use ellalgo_rs::ell_calc::EllCalc;
816    /// use ellalgo_rs::cutting_plane::CutStatus;
817    ///
818    /// let ell_calc = EllCalc::new(4);
819    /// let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(0.01);
820    ///
821    /// assert_eq!(status, CutStatus::Success);
822    /// assert_approx_eq!(sigma, 0.4);
823    /// assert_approx_eq!(rho, 0.02);
824    /// assert_approx_eq!(delta, 16.0 / 15.0);
825    /// ```
826    #[inline]
827    pub fn calc_central_cut(&self, tsq: f64) -> (CutStatus, (f64, f64, f64)) {
828        // self.mu = self.half_n_minus_1;
829        (CutStatus::Success, self.helper.calc_central_cut(tsq))
830    }
831}
832
833// pub trait UpdateByCutChoice {
834//     fn update_by(self, ell: &mut EllCalc) -> CutStatus;
835// }
836#[cfg(test)]
837mod tests {
838    use super::*;
839    use approx_eq::assert_approx_eq;
840
841    #[test]
842    pub fn test_construct() {
843        let helper = EllCalcCore::new(4.0);
844        assert_eq!(helper.n_f, 4.0);
845        assert_eq!(helper.half_n, 2.0);
846        assert_approx_eq!(helper.cst1, 16.0 / 15.0);
847        assert_approx_eq!(helper.cst2, 0.4);
848    }
849
850    #[test]
851    pub fn test_calc_parallel_bias_cut_fast() {
852        let ell_calc_core = EllCalcCore::new(4.0);
853        let (rho, sigma, delta) =
854            ell_calc_core.calc_parallel_bias_cut_fast(1.0, 2.0, 4.0, 2.0, 12.0);
855        assert_approx_eq!(rho, 1.2);
856        assert_approx_eq!(sigma, 0.8);
857        assert_approx_eq!(delta, 0.8);
858    }
859
860    #[test]
861    pub fn test_calc_bias_cut_fast() {
862        let ell_calc_core = EllCalcCore::new(4.0);
863        let (rho, sigma, delta) = ell_calc_core.calc_bias_cut_fast(1.0, 2.0, 6.0);
864        assert_approx_eq!(rho, 1.2);
865        assert_approx_eq!(sigma, 0.8);
866        assert_approx_eq!(delta, 0.8);
867    }
868
869    #[test]
870    pub fn test_calc_central_cut() {
871        let ell_calc = EllCalc::new(4);
872        // ell_calc.tsq = 0.01;
873        let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(0.01);
874        assert_eq!(status, CutStatus::Success);
875        assert_approx_eq!(sigma, 0.4);
876        assert_approx_eq!(rho, 0.02);
877        assert_approx_eq!(delta, 16.0 / 15.0);
878    }
879
880    #[test]
881    pub fn test_calc_bias_cut() {
882        let ell_calc = EllCalc::new(4);
883        // ell_calc.tsq = 0.01;
884        let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
885        assert_eq!(status, CutStatus::NoSoln);
886        let (status, _result) = ell_calc.calc_bias_cut(0.0, 0.01);
887        assert_eq!(status, CutStatus::Success);
888        let (status, _result) = ell_calc.calc_bias_cut_q(-0.05, 0.01);
889        assert_eq!(status, CutStatus::NoEffect);
890
891        // ell_calc.tsq = 0.01;
892        let (status, (rho, sigma, delta)) = ell_calc.calc_bias_cut(0.05, 0.01);
893        assert_eq!(status, CutStatus::Success);
894        assert_approx_eq!(sigma, 0.8);
895        assert_approx_eq!(rho, 0.06);
896        assert_approx_eq!(delta, 0.8);
897    }
898
899    #[test]
900    pub fn test_calc_parallel_central_cut() {
901        let ell_calc = EllCalc::new(4);
902        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, 0.01);
903        assert_eq!(status, CutStatus::Success);
904        assert_approx_eq!(sigma, 0.4);
905        assert_approx_eq!(rho, 0.02);
906        assert_approx_eq!(delta, 16.0 / 15.0);
907
908        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, 0.01);
909        assert_eq!(status, CutStatus::Success);
910        assert_approx_eq!(sigma, 0.8);
911        assert_approx_eq!(rho, 0.02);
912        assert_approx_eq!(delta, 1.2);
913    }
914
915    #[test]
916    pub fn test_calc_parallel() {
917        let ell_calc = EllCalc::new(4);
918        // ell_calc.tsq = 0.01;
919        let (status, _result) = ell_calc.calc_parallel_bias_cut(0.07, 0.03, 0.01);
920        assert_eq!(status, CutStatus::NoSoln);
921
922        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.0, 0.05, 0.01);
923        assert_eq!(status, CutStatus::Success);
924        assert_approx_eq!(sigma, 0.8);
925        assert_approx_eq!(rho, 0.02);
926        assert_approx_eq!(delta, 1.2);
927
928        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.05, 0.11, 0.01);
929        assert_eq!(status, CutStatus::Success);
930        assert_approx_eq!(sigma, 0.8);
931        assert_approx_eq!(rho, 0.06);
932        assert_approx_eq!(delta, 0.8);
933
934        let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, 0.01);
935        assert_eq!(status, CutStatus::NoEffect);
936
937        let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_bias_cut(0.01, 0.04, 0.01);
938        assert_eq!(status, CutStatus::Success);
939        assert_approx_eq!(sigma, 0.928);
940        assert_approx_eq!(rho, 0.0232);
941        assert_approx_eq!(delta, 1.232);
942
943        let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, 0.01);
944        assert_eq!(status, CutStatus::NoEffect);
945    }
946
947    #[test]
948    fn test_construct2() {
949        let ell_calc = EllCalc::new(4);
950        assert!(ell_calc.use_parallel_cut);
951        assert_eq!(ell_calc.n_f, 4.0);
952    }
953
954    #[test]
955    fn test_calc_central_cut2() {
956        let ell_calc = EllCalc::new(4);
957        let (status, result) =
958            ell_calc.calc_single_or_parallel_central_cut(&(0.0, Some(0.05)), 0.01);
959        assert_eq!(status, CutStatus::Success);
960        let (rho, sigma, delta) = result;
961        assert_approx_eq!(sigma, 0.8);
962        assert_approx_eq!(rho, 0.02);
963        assert_approx_eq!(delta, 1.2);
964    }
965
966    #[test]
967    fn test_calc_bias_cut2() {
968        let ell_calc = EllCalc::new(4);
969        let (status, _result) = ell_calc.calc_bias_cut(0.11, 0.01);
970        assert_eq!(status, CutStatus::NoSoln);
971        let (status, _result) = ell_calc.calc_bias_cut(0.01, 0.01);
972        assert_eq!(status, CutStatus::Success);
973
974        let (status, result) = ell_calc.calc_bias_cut(0.05, 0.01);
975        assert_eq!(status, CutStatus::Success);
976        let (rho, sigma, delta) = result;
977        assert_approx_eq!(rho, 0.06);
978        assert_approx_eq!(sigma, 0.8);
979        assert_approx_eq!(delta, 0.8);
980    }
981
982    #[test]
983    fn test_calc_parallel_central_cut2() {
984        let ell_calc = EllCalc::new(4);
985        let (status, result) =
986            ell_calc.calc_single_or_parallel_central_cut(&(0.0, Some(0.05)), 0.01);
987        assert_eq!(status, CutStatus::Success);
988        let (rho, sigma, delta) = result;
989        assert_approx_eq!(rho, 0.02);
990        assert_approx_eq!(sigma, 0.8);
991        assert_approx_eq!(delta, 1.2);
992    }
993
994    #[test]
995    fn test_calc_parallel2() {
996        let ell_calc = EllCalc::new(4);
997        let (status, _result) = ell_calc.calc_parallel_bias_cut(0.07, 0.03, 0.01);
998        assert_eq!(status, CutStatus::NoSoln);
999        let (status, result) = ell_calc.calc_parallel_bias_cut(0.0, 0.05, 0.01);
1000        assert_eq!(status, CutStatus::Success);
1001        let (rho, sigma, delta) = result;
1002        assert_approx_eq!(rho, 0.02);
1003        assert_approx_eq!(sigma, 0.8);
1004        assert_approx_eq!(delta, 1.2);
1005        let (status, result) = ell_calc.calc_parallel_bias_cut(0.05, 0.11, 0.01);
1006        assert_eq!(status, CutStatus::Success);
1007        let (rho, sigma, delta) = result;
1008        assert_approx_eq!(rho, 0.06);
1009        assert_approx_eq!(sigma, 0.8);
1010        assert_approx_eq!(delta, 0.8);
1011        let (status, result) = ell_calc.calc_parallel_bias_cut(0.01, 0.04, 0.01);
1012        assert_eq!(status, CutStatus::Success);
1013        let (rho, sigma, delta) = result;
1014        assert_approx_eq!(rho, 0.0232);
1015        assert_approx_eq!(sigma, 0.928);
1016        assert_approx_eq!(delta, 1.232);
1017    }
1018
1019    #[test]
1020    fn test_calc_bias_cut_q2() {
1021        let ell_calc_q = EllCalc::new(4);
1022        let (status, _result) = ell_calc_q.calc_bias_cut_q(0.11, 0.01);
1023        assert_eq!(status, CutStatus::NoSoln);
1024        let (status, _result) = ell_calc_q.calc_bias_cut_q(0.01, 0.01);
1025        assert_eq!(status, CutStatus::Success);
1026        let (status, _result) = ell_calc_q.calc_bias_cut_q(-0.05, 0.01);
1027        assert_eq!(status, CutStatus::NoEffect);
1028        let (status, result) = ell_calc_q.calc_bias_cut_q(0.05, 0.01);
1029        assert_eq!(status, CutStatus::Success);
1030        let (rho, sigma, delta) = result;
1031        assert_approx_eq!(rho, 0.06);
1032        assert_approx_eq!(sigma, 0.8);
1033        assert_approx_eq!(delta, 0.8);
1034    }
1035
1036    #[test]
1037    fn test_calc_parallel_q2() {
1038        let ell_calc = EllCalc::new(4);
1039        let (status, _result) = ell_calc.calc_parallel_q(0.07, 0.03, 0.01);
1040        assert_eq!(status, CutStatus::NoSoln);
1041        let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, 0.01);
1042        assert_eq!(status, CutStatus::NoEffect);
1043        let (status, result) = ell_calc.calc_parallel_q(0.0, 0.05, 0.01);
1044        assert_eq!(status, CutStatus::Success);
1045        let (rho, sigma, delta) = result;
1046        assert_approx_eq!(rho, 0.02);
1047        assert_approx_eq!(sigma, 0.8);
1048        assert_approx_eq!(delta, 1.2);
1049        let (status, result) = ell_calc.calc_parallel_q(0.05, 0.11, 0.01);
1050        assert_eq!(status, CutStatus::Success);
1051        let (rho, sigma, delta) = result;
1052        assert_approx_eq!(rho, 0.06);
1053        assert_approx_eq!(sigma, 0.8);
1054        assert_approx_eq!(delta, 0.8);
1055        let (status, result) = ell_calc.calc_parallel_q(0.01, 0.04, 0.01);
1056        assert_eq!(status, CutStatus::Success);
1057        let (rho, sigma, delta) = result;
1058        assert_approx_eq!(rho, 0.0232);
1059        assert_approx_eq!(sigma, 0.928);
1060        assert_approx_eq!(delta, 1.232);
1061    }
1062}