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 cst1: f64,
27 cst2: f64,
28}
29
30impl EllCalcCore {
31 /// The `new` function constructs a new [`EllCalcCore`] object with the given parameter `n_f` and
32 /// initializes its internal variables.
33 ///
34 /// Arguments:
35 ///
36 /// * `n_f`: The parameter `n_f` represents the value of `n` in the calculations. It is a floating-point
37 /// number.
38 ///
39 /// Returns:
40 ///
41 /// The `new` function returns an instance of the `EllCalcCore` struct.
42 ///
43 /// # Examples:
44 ///
45 /// ```rust
46 /// use approx_eq::assert_approx_eq;
47 /// use ellalgo_rs::ell_calc::EllCalcCore;
48 ///
49 /// let ell_calc_core = EllCalcCore::new(4.0);
50 ///
51 /// assert_approx_eq!(ell_calc_core.n_f, 4.0);
52 /// assert_approx_eq!(ell_calc_core.half_n, 2.0);
53 /// assert_approx_eq!(ell_calc_core.n_plus_1, 5.0);
54 /// ```
55 pub fn new(n_f: f64) -> EllCalcCore {
56 let n_plus_1 = n_f + 1.0;
57 let half_n = n_f / 2.0;
58 let n_sq = n_f * n_f;
59 let cst0 = 1.0 / (n_f + 1.0);
60 let cst1 = n_sq / (n_sq - 1.0);
61 let cst2 = 2.0 * cst0;
62
63 EllCalcCore {
64 n_f,
65 n_plus_1,
66 half_n,
67 cst1,
68 cst2,
69 }
70 }
71
72 #[doc = svgbobdoc::transform!(
73 /// The function calculates the core values for updating an ellipsoid with either a parallel-cut or
74 /// a deep-cut.
75 ///
76 /// Arguments:
77 ///
78 /// * `beta0`: The parameter `beta0` represents the semi-minor axis of the ellipsoid before the cut. It is
79 /// a floating-point number.
80 /// * `beta1`: The parameter `beta1` represents the length of the semi-minor axis of the ellipsoid.
81 /// * `tsq`: tsq is a reference to a f64 value, which represents the square of the semi-major axis
82 /// of the ellipsoid.
83 ///
84 /// ```svgbob
85 /// _.-'''''''-._
86 /// ,' | `.
87 /// / | | \
88 /// . | | .
89 /// | | | |
90 /// | | |. |
91 /// | | | |
92 /// :\ | | /:
93 /// | `._ | _.' |
94 /// | |'-.......-' |
95 /// | | | |
96 /// "-τ" "-β" "-β" +τ
97 /// 1 0
98 /// ```
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// use approx_eq::assert_approx_eq;
104 /// use ellalgo_rs::ell_calc::EllCalcCore;
105 ///
106 /// let ell_calc_core = EllCalcCore::new(4.0);
107 /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_bias_cut(1.0, 2.0, &4.0);
108 /// assert_approx_eq!(rho, 1.2);
109 /// assert_approx_eq!(sigma, 0.8);
110 /// assert_approx_eq!(delta, 0.8);
111 /// ```
112 )]
113 #[inline]
114 pub fn calc_parallel_bias_cut(&self, beta0: f64, beta1: f64, tsq: &f64) -> (f64, f64, f64) {
115 let b0b1 = beta0 * beta1;
116 let eta = tsq + self.n_f * b0b1;
117 self.calc_parallel_bias_cut_fast(beta0, beta1, tsq, &b0b1, &eta)
118 }
119
120 pub fn calc_parallel_bias_cut_fast(
121 &self,
122 beta0: f64,
123 beta1: f64,
124 tsq: &f64,
125 b0b1: &f64,
126 eta: &f64,
127 ) -> (f64, f64, f64) {
128 let bsum = beta0 + beta1;
129 let bsumsq = bsum * bsum;
130 let h = tsq + b0b1 + self.half_n * bsumsq;
131 let root = h + (h * h - eta * self.n_plus_1 * bsumsq).sqrt();
132 let inv_mu_plus_2 = eta / root;
133 let inv_mu = eta / (root - 2.0 * eta);
134 let rho = bsum * inv_mu_plus_2;
135 let sigma = 2.0 * inv_mu_plus_2;
136 let delta = 1.0 + (-2.0 * b0b1 + bsumsq * inv_mu_plus_2) * inv_mu / tsq;
137
138 (rho, sigma, delta)
139 }
140
141 #[doc = svgbobdoc::transform!(
142 /// The function calculates the core values for updating an ellipsoid with the parallel-cut method.
143 ///
144 /// Arguments:
145 ///
146 /// * `beta1`: The parameter `beta1` represents the semi-minor axis of the ellipsoid. It is a floating-point
147 /// number.
148 /// * `tsq`: The parameter `tsq` represents the square of the gamma semi-axis length of the ellipsoid.
149 ///
150 /// ```svgbob
151 /// _.-'''''''-._
152 /// ,' | `.
153 /// / | | \
154 /// . | | .
155 /// | | |
156 /// | | . |
157 /// | | |
158 /// :\ | | /:
159 /// | `._ | _.' |
160 /// | |'-.......-' |
161 /// | | | |
162 /// "-τ" "-β" 0 +τ
163 /// 1
164 ///
165 /// 2 2 2
166 /// α = β / τ
167 ///
168 /// n 2
169 /// h = ─ ⋅ α
170 /// 2
171 /// _____________
172 /// ╱ 2 2
173 /// r = h + ╲╱ h + 1.0 - α
174 ///
175 /// β
176 /// ϱ = ─────
177 /// r + 1
178 ///
179 /// 2
180 /// σ = ─────
181 /// r + 1
182 ///
183 /// n ⋅ r
184 /// δ = ─────────
185 /// n ⋅ r - 1
186 /// ```
187 ///
188 /// # Example
189 ///
190 /// ```
191 /// use approx_eq::assert_approx_eq;
192 /// use ellalgo_rs::ell_calc::EllCalcCore;
193 ///
194 /// let ell_calc_core = EllCalcCore::new(4.0);
195 /// let (rho, sigma, delta) = ell_calc_core.calc_parallel_central_cut(1.0, &4.0);
196 /// assert_approx_eq!(rho, 0.4);
197 /// assert_approx_eq!(sigma, 0.8);
198 /// assert_approx_eq!(delta, 1.2);
199 /// ```
200 )]
201 pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: &f64) -> (f64, f64, f64) {
202 let b1sq = beta1 * beta1;
203 let a1sq = b1sq / tsq;
204 let temp = self.half_n * a1sq;
205 let mu_plus_1 = temp + (1.0 - a1sq + temp * temp).sqrt();
206 let mu_plus_2 = mu_plus_1 + 1.0;
207 let rho = beta1 / mu_plus_2;
208 let sigma = 2.0 / mu_plus_2;
209 let temp2 = self.n_f * mu_plus_1;
210 let delta = temp2 / (temp2 - 1.0);
211
212 (rho, sigma, delta)
213 }
214
215 #[doc = svgbobdoc::transform!(
216 /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
217 ///
218 /// Arguments:
219 ///
220 /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
221 /// the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
222 /// number.
223 /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
224 /// quickly the system responds to changes.
225 /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
226 /// ellipsoid is being updated or modified.
227 ///
228 /// ```svgbob
229 /// _.-'''''''-._
230 /// ,' | `.
231 /// / | \
232 /// . | .
233 /// | | |
234 /// | | . |
235 /// | | |
236 /// :\ | /:
237 /// | `._ | _.' |
238 /// | '-.......-' |
239 /// | | |
240 /// "-τ" "-β" +τ
241 ///
242 /// η = τ + n ⋅ β
243 ///
244 /// η
245 /// ϱ = ─────
246 /// n + 1
247 ///
248 /// 2 ⋅ ϱ
249 /// σ = ─────
250 /// "τ + β"
251 ///
252 /// 2 2 2
253 /// n τ - β
254 /// δ = ────── ⋅ ───────
255 /// 2 2
256 /// n - 1 τ
257 /// ```
258 ///
259 /// # Example
260 ///
261 /// ```
262 /// use approx_eq::assert_approx_eq;
263 /// use ellalgo_rs::ell_calc::EllCalcCore;
264 ///
265 /// let ell_calc_core = EllCalcCore::new(4.0);
266 /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut(&1.0, &2.0);
267 /// assert_approx_eq!(rho, 1.2);
268 /// assert_approx_eq!(sigma, 0.8);
269 /// assert_approx_eq!(delta, 0.8);
270 /// ```
271 )]
272 #[inline]
273 pub fn calc_bias_cut(&self, beta: &f64, tau: &f64) -> (f64, f64, f64) {
274 let eta = tau + self.n_f * beta;
275 self.calc_bias_cut_fast(beta, tau, &eta)
276 }
277
278 #[doc = svgbobdoc::transform!(
279 /// The function calculates the core values needed for updating an ellipsoid with the deep-cut method.
280 ///
281 /// Arguments:
282 ///
283 /// * `beta`: The `beta` parameter represents a value used in the calculation of the core of updating
284 /// the ellipsoid with the deep-cut. It is of type `f64`, which means it is a 64-bit floating-point
285 /// number.
286 /// * `tau`: The parameter `tau` represents the time constant of the system. It is a measure of how
287 /// quickly the system responds to changes.
288 /// * `eta`: The parameter `eta` represents the deep-cut factor. It is a measure of how much the
289 /// ellipsoid is being updated or modified.
290 ///
291 /// ```svgbob
292 /// _.-'''''''-._
293 /// ,' | `.
294 /// / | \
295 /// . | .
296 /// | | |
297 /// | | . |
298 /// | | |
299 /// :\ | /:
300 /// | `._ | _.' |
301 /// | '-.......-' |
302 /// | | |
303 /// "-τ" "-β" +τ
304 ///
305 /// η
306 /// ϱ = ─────
307 /// n + 1
308 ///
309 /// 2 ⋅ ϱ
310 /// σ = ─────
311 /// "τ + β"
312 ///
313 /// 2 2 2
314 /// n τ - β
315 /// δ = ────── ⋅ ───────
316 /// 2 2
317 /// n - 1 τ
318 /// ```
319 ///
320 /// # Example
321 ///
322 /// ```
323 /// use approx_eq::assert_approx_eq;
324 /// use ellalgo_rs::ell_calc::EllCalcCore;
325 ///
326 /// let ell_calc_core = EllCalcCore::new(4.0);
327 /// let (rho, sigma, delta) = ell_calc_core.calc_bias_cut_fast(&1.0, &2.0, &6.0);
328 /// assert_approx_eq!(rho, 1.2);
329 /// assert_approx_eq!(sigma, 0.8);
330 /// assert_approx_eq!(delta, 0.8);
331 /// ```
332 )]
333 pub fn calc_bias_cut_fast(&self, beta: &f64, tau: &f64, eta: &f64) -> (f64, f64, f64) {
334 let rho = eta / self.n_plus_1;
335 let sigma = 2.0 * rho / (tau + beta);
336 let alpha = beta / tau;
337 let delta = self.cst1 * (1.0 - alpha * alpha);
338 (rho, sigma, delta)
339 }
340
341 #[doc = svgbobdoc::transform!(
342 /// The `calc_central_cut_core` function calculates the core values needed for updating an ellipsoid with the
343 /// central-cut.
344 ///
345 /// Arguments:
346 ///
347 /// * `tsq`: The parameter `tsq` represents the square of the time taken to update the ellipsoid with
348 /// the central-cut.
349 ///
350 /// ```svgbob
351 /// _.-'''''''-._
352 /// ,' | `.
353 /// / | \
354 /// . | .
355 /// | |
356 /// | . |
357 /// | |
358 /// :\ | /:
359 /// | `._ | _.' |
360 /// | '-.......-' |
361 /// | | |
362 /// "-τ" 0 +τ
363 ///
364 /// 2
365 /// σ = ─────
366 /// n + 1
367 ///
368 /// τ
369 /// ϱ = ─────
370 /// n + 1
371 ///
372 /// 2
373 /// n
374 /// δ = ──────
375 /// 2
376 /// n - 1
377 /// ```
378 ///
379 /// # Example
380 ///
381 /// ```rust
382 /// use approx_eq::assert_approx_eq;
383 /// use ellalgo_rs::ell_calc::EllCalcCore;
384 /// let ell_calc_core = EllCalcCore::new(4.0);
385 /// let (rho, sigma, delta) = ell_calc_core.calc_central_cut(&4.0);
386 /// assert_approx_eq!(rho, 0.4);
387 /// assert_approx_eq!(sigma, 0.4);
388 /// assert_approx_eq!(delta, 16.0/15.0);
389 /// ```
390 )]
391 pub fn calc_central_cut(&self, tsq: &f64) -> (f64, f64, f64) {
392 // self.mu = self.half_n_minus_1;
393 let sigma = self.cst2;
394 let rho = tsq.sqrt() / self.n_plus_1;
395 let delta = self.cst1;
396 (rho, sigma, delta)
397 }
398}
399
400/// The `EllCalc` struct represents an ellipsoid search space in Rust.
401///
402/// EllCalc = {x | (x - xc)^T mq^-1 (x - xc) \le \kappa}
403///
404/// Properties:
405///
406/// * `n_f`: The `n_f` property is a floating-point number that represents the dimensionality of the
407/// search space. It indicates the number of variables or dimensions in the ellipsoid search space.
408/// * `helper`: The `helper` property is of type `EllCalcCore` and is used to perform
409/// calculations related to the ellipsoid search space. It is a separate struct that contains the
410/// necessary methods and data for these calculations.
411/// * `use_parallel_cut`: A boolean flag indicating whether to use parallel cut or not.
412#[derive(Debug, Clone)]
413pub struct EllCalc {
414 n_f: f64,
415 pub helper: EllCalcCore,
416 pub use_parallel_cut: bool,
417}
418
419impl EllCalc {
420 /// The `new` function constructs a new [`EllCalc`] object with a given value for `n_f` and sets the
421 /// `use_parallel_cut` flag to `true`.
422 ///
423 /// Arguments:
424 ///
425 /// * `n_f`: The parameter `n_f` is a floating-point number that is used to initialize the `EllCalc`
426 /// struct.
427 ///
428 /// Returns:
429 ///
430 /// The `new` function is returning an instance of the `EllCalc` struct.
431 ///
432 /// # Examples:
433 ///
434 /// ```rust
435 /// use ellalgo_rs::ell_calc::EllCalc;
436 /// let ell_calc = EllCalc::new(4.0);
437 /// assert!(ell_calc.use_parallel_cut);
438 /// ```
439 pub fn new(n_f: f64) -> EllCalc {
440 let helper = EllCalcCore::new(n_f);
441
442 EllCalc {
443 n_f,
444 helper,
445 use_parallel_cut: true,
446 }
447 }
448
449 // pub fn update_cut(&mut self, beta: f64) -> CutStatus { self.calc_deep_cut(beta) }
450
451 /// The function calculates the updating of an ellipsoid with a single or parallel-cut.
452 ///
453 /// Arguments:
454 ///
455 /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
456 /// `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
457 /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
458 pub fn calc_single_or_parallel_deep_cut(
459 &self,
460 beta: &(f64, Option<f64>),
461 tsq: &f64,
462 ) -> (CutStatus, (f64, f64, f64)) {
463 let (beta0, beta1_opt) = *beta;
464 if let Some(beta1) = beta1_opt {
465 self.calc_parallel_deep_cut(beta0, beta1, tsq)
466 } else {
467 self.calc_deep_cut(&beta0, tsq)
468 }
469 }
470
471 /// The function calculates the updating of an ellipsoid with a single or parallel-cut (one of them is central-cut).
472 ///
473 /// Arguments:
474 ///
475 /// * `beta`: The `beta` parameter is a tuple containing two values: `f64` and `Option<f64>`.
476 /// The first value, denoted as `_b0`, is of type `f64`. The second value, `beta1_opt`, is of type `Option<f64>`.
477 /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
478 pub fn calc_single_or_parallel_central_cut(
479 &self,
480 beta: &(f64, Option<f64>),
481 tsq: &f64,
482 ) -> (CutStatus, (f64, f64, f64)) {
483 let (_b0, beta1_opt) = *beta;
484 if let Some(beta1) = beta1_opt {
485 self.calc_parallel_central_cut(beta1, tsq)
486 } else {
487 self.calc_central_cut(tsq)
488 }
489 }
490
491 /// The function calculates the updating of an ellipsoid with a single or parallel-cut (discrete version).
492 ///
493 /// Arguments:
494 ///
495 /// * `beta`: The `beta` parameter is a tuple containing two values: `beta0` and `beta1_opt`. `beta0` is of type
496 /// `f64` and `beta1_opt` is an optional value of type `Option<f64>`.
497 /// * `tsq`: The `tsq` parameter is a reference to a `f64` value.
498 pub fn calc_single_or_parallel_q(
499 &self,
500 beta: &(f64, Option<f64>),
501 tsq: &f64,
502 ) -> (CutStatus, (f64, f64, f64)) {
503 let (beta0, beta1_opt) = *beta;
504 if let Some(beta1) = beta1_opt {
505 self.calc_parallel_q(beta0, beta1, tsq)
506 } else {
507 self.calc_bias_cut_q(&beta0, tsq)
508 }
509 }
510
511 /// Parallel Deep Cut
512 ///
513 /// # Examples:
514 ///
515 /// ```rust
516 /// use approx_eq::assert_approx_eq;
517 /// use ellalgo_rs::ell_calc::EllCalc;
518 /// use ellalgo_rs::cutting_plane::CutStatus;
519 ///
520 /// let ell_calc = EllCalc::new(4.0);
521 /// let (status, _result) = ell_calc.calc_parallel_deep_cut(0.07, 0.03, &0.01);
522 /// assert_eq!(status, CutStatus::NoSoln);
523 ///
524 /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.0, 0.05, &0.01);
525 /// assert_eq!(status, CutStatus::Success);
526 /// assert_approx_eq!(sigma, 0.8);
527 /// assert_approx_eq!(rho, 0.02);
528 /// assert_approx_eq!(delta, 1.2);
529 ///
530 /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.05, 0.11, &0.01);
531 /// assert_eq!(status, CutStatus::Success);
532 /// assert_approx_eq!(sigma, 0.8);
533 /// assert_approx_eq!(rho, 0.06);
534 /// assert_approx_eq!(delta, 0.8);
535 ///
536 /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.01, 0.04, &0.01);
537 /// assert_eq!(status, CutStatus::Success);
538 /// assert_approx_eq!(sigma, 0.928);
539 /// assert_approx_eq!(rho, 0.0232);
540 /// assert_approx_eq!(delta, 1.232);
541 /// ```
542 pub fn calc_parallel_deep_cut(
543 &self,
544 beta0: f64,
545 beta1: f64,
546 tsq: &f64,
547 ) -> (CutStatus, (f64, f64, f64)) {
548 if beta1 < beta0 {
549 return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
550 }
551
552 let b1sqn = beta1 * (beta1 / tsq);
553 let t1n = 1.0 - b1sqn;
554 if t1n < 0.0 || !self.use_parallel_cut {
555 return self.calc_deep_cut(&beta0, tsq);
556 }
557
558 (
559 CutStatus::Success,
560 self.helper.calc_parallel_bias_cut(beta0, beta1, tsq),
561 )
562 }
563
564 /// Discrete Parallel Deep Cut
565 ///
566 /// # Examples:
567 ///
568 /// ```rust
569 /// use approx_eq::assert_approx_eq;
570 /// use ellalgo_rs::ell_calc::EllCalc;
571 /// use ellalgo_rs::cutting_plane::CutStatus;
572 ///
573 /// let ell_calc = EllCalc::new(4.0);
574 /// let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, &0.01);
575 /// assert_eq!(status, CutStatus::NoEffect);
576 ///
577 /// let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, &0.01);
578 /// assert_eq!(status, CutStatus::NoEffect);
579 /// ```
580 pub fn calc_parallel_q(
581 &self,
582 beta0: f64,
583 beta1: f64,
584 tsq: &f64,
585 ) -> (CutStatus, (f64, f64, f64)) {
586 if beta1 < beta0 {
587 return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
588 }
589
590 if (beta1 > 0.0) && &(beta1 * beta1) >= tsq || !self.use_parallel_cut {
591 return self.calc_bias_cut_q(&beta0, tsq);
592 }
593
594 let b0b1 = beta0 * beta1;
595 let eta = tsq + self.n_f * b0b1;
596 if eta <= 0.0 {
597 return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
598 }
599
600 (
601 CutStatus::Success,
602 self.helper
603 .calc_parallel_bias_cut_fast(beta0, beta1, tsq, &b0b1, &eta),
604 )
605 }
606
607 /// Parallel Central Cut
608 ///
609 /// # Examples:
610 ///
611 /// ```rust
612 /// use approx_eq::assert_approx_eq;
613 /// use ellalgo_rs::ell_calc::EllCalc;
614 /// use ellalgo_rs::cutting_plane::CutStatus;
615 ///
616 /// let ell_calc = EllCalc::new(4.0);
617 /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, &0.01);
618 /// assert_eq!(status, CutStatus::Success);
619 /// assert_approx_eq!(sigma, 0.4);
620 /// assert_approx_eq!(rho, 0.02);
621 /// assert_approx_eq!(delta, 16.0 / 15.0);
622 ///
623 /// let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, &0.01);
624 /// assert_eq!(status, CutStatus::Success);
625 /// assert_approx_eq!(sigma, 0.8);
626 /// assert_approx_eq!(rho, 0.02);
627 /// assert_approx_eq!(delta, 1.2);
628 /// ```
629 pub fn calc_parallel_central_cut(&self, beta1: f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
630 if beta1 < 0.0 {
631 return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no solution
632 }
633 let b1sqn = beta1 * (beta1 / tsq);
634 let t1n = 1.0 - b1sqn;
635 if t1n < 0.0 || !self.use_parallel_cut {
636 return self.calc_central_cut(tsq);
637 }
638 (
639 CutStatus::Success,
640 self.helper.calc_parallel_central_cut(beta1, tsq),
641 )
642 }
643
644 /// Deep Cut
645 ///
646 /// # Examples:
647 ///
648 /// ```rust
649 /// use approx_eq::assert_approx_eq;
650 /// use ellalgo_rs::ell_calc::EllCalc;
651 /// use ellalgo_rs::cutting_plane::CutStatus;
652 ///
653 /// let ell_calc = EllCalc::new(4.0);
654 /// let (status, _result) = ell_calc.calc_deep_cut(&0.11, &0.01);
655 /// assert_eq!(status, CutStatus::NoSoln);
656 /// let (status, _result) = ell_calc.calc_deep_cut(&0.0, &0.01);
657 /// assert_eq!(status, CutStatus::Success);
658 ///
659 /// let (status, (rho, sigma, delta)) = ell_calc.calc_deep_cut(&0.05, &0.01);
660 /// assert_eq!(status, CutStatus::Success);
661 /// assert_approx_eq!(sigma, 0.8);
662 /// assert_approx_eq!(rho, 0.06);
663 /// assert_approx_eq!(delta, 0.8);
664 /// ```
665 pub fn calc_deep_cut(&self, beta: &f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
666 if *tsq < beta * beta {
667 return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
668 }
669
670 let tau = tsq.sqrt();
671 (CutStatus::Success, self.helper.calc_bias_cut(beta, &tau))
672 }
673
674 /// Discrete Deep Cut
675 ///
676 /// # Examples:
677 ///
678 /// ```rust
679 /// use approx_eq::assert_approx_eq;
680 /// use ellalgo_rs::ell_calc::EllCalc;
681 /// use ellalgo_rs::cutting_plane::CutStatus;
682 ///
683 /// let ell_calc = EllCalc::new(4.0);
684 /// let (status, _result) = ell_calc.calc_bias_cut_q(&-0.05, &0.01);
685 /// assert_eq!(status, CutStatus::NoEffect);
686 /// ```
687 pub fn calc_bias_cut_q(&self, beta: &f64, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
688 let tau = tsq.sqrt();
689
690 if tau < *beta {
691 return (CutStatus::NoSoln, (0.0, 0.0, 0.0)); // no sol'n
692 }
693
694 let eta = tau + self.n_f * beta;
695 if eta < 0.0 {
696 return (CutStatus::NoEffect, (0.0, 0.0, 1.0)); // no effect
697 }
698
699 (
700 CutStatus::Success,
701 self.helper.calc_bias_cut_fast(beta, &tau, &eta),
702 )
703 }
704
705 /// Central Cut
706 ///
707 /// # Examples:
708 ///
709 /// ```rust
710 /// use approx_eq::assert_approx_eq;
711 /// use ellalgo_rs::ell_calc::EllCalc;
712 /// use ellalgo_rs::cutting_plane::CutStatus;
713 ///
714 /// let ell_calc = EllCalc::new(4.0);
715 /// let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(&0.01);
716 ///
717 /// assert_eq!(status, CutStatus::Success);
718 /// assert_approx_eq!(sigma, 0.4);
719 /// assert_approx_eq!(rho, 0.02);
720 /// assert_approx_eq!(delta, 16.0 / 15.0);
721 /// ```
722 #[inline]
723 pub fn calc_central_cut(&self, tsq: &f64) -> (CutStatus, (f64, f64, f64)) {
724 // self.mu = self.half_n_minus_1;
725 (CutStatus::Success, self.helper.calc_central_cut(tsq))
726 }
727}
728
729// pub trait UpdateByCutChoices {
730// fn update_by(self, ell: &mut EllCalc) -> CutStatus;
731// }
732#[cfg(test)]
733mod tests {
734 use super::*;
735 use approx_eq::assert_approx_eq;
736
737 #[test]
738 pub fn test_construct() {
739 let helper = EllCalcCore::new(4.0);
740 assert_eq!(helper.n_f, 4.0);
741 assert_eq!(helper.half_n, 2.0);
742 assert_approx_eq!(helper.cst1, 16.0 / 15.0);
743 assert_approx_eq!(helper.cst2, 0.4);
744 }
745
746 #[test]
747 pub fn test_calc_central_cut() {
748 let ell_calc = EllCalc::new(4.0);
749 // ell_calc.tsq = 0.01;
750 let (status, (rho, sigma, delta)) = ell_calc.calc_central_cut(&0.01);
751 assert_eq!(status, CutStatus::Success);
752 assert_approx_eq!(sigma, 0.4);
753 assert_approx_eq!(rho, 0.02);
754 assert_approx_eq!(delta, 16.0 / 15.0);
755 }
756
757 #[test]
758 pub fn test_calc_deep_cut() {
759 let ell_calc = EllCalc::new(4.0);
760 // ell_calc.tsq = 0.01;
761 let (status, _result) = ell_calc.calc_deep_cut(&0.11, &0.01);
762 assert_eq!(status, CutStatus::NoSoln);
763 let (status, _result) = ell_calc.calc_deep_cut(&0.0, &0.01);
764 assert_eq!(status, CutStatus::Success);
765 let (status, _result) = ell_calc.calc_bias_cut_q(&-0.05, &0.01);
766 assert_eq!(status, CutStatus::NoEffect);
767
768 // ell_calc.tsq = 0.01;
769 let (status, (rho, sigma, delta)) = ell_calc.calc_deep_cut(&0.05, &0.01);
770 assert_eq!(status, CutStatus::Success);
771 assert_approx_eq!(sigma, 0.8);
772 assert_approx_eq!(rho, 0.06);
773 assert_approx_eq!(delta, 0.8);
774 }
775
776 #[test]
777 pub fn test_calc_parallel_central_cut() {
778 let ell_calc = EllCalc::new(4.0);
779 let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.11, &0.01);
780 assert_eq!(status, CutStatus::Success);
781 assert_approx_eq!(sigma, 0.4);
782 assert_approx_eq!(rho, 0.02);
783 assert_approx_eq!(delta, 16.0 / 15.0);
784
785 let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_central_cut(0.05, &0.01);
786 assert_eq!(status, CutStatus::Success);
787 assert_approx_eq!(sigma, 0.8);
788 assert_approx_eq!(rho, 0.02);
789 assert_approx_eq!(delta, 1.2);
790 }
791
792 #[test]
793 pub fn test_calc_parallel() {
794 let ell_calc = EllCalc::new(4.0);
795 // ell_calc.tsq = 0.01;
796 let (status, _result) = ell_calc.calc_parallel_deep_cut(0.07, 0.03, &0.01);
797 assert_eq!(status, CutStatus::NoSoln);
798
799 let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.0, 0.05, &0.01);
800 assert_eq!(status, CutStatus::Success);
801 assert_approx_eq!(sigma, 0.8);
802 assert_approx_eq!(rho, 0.02);
803 assert_approx_eq!(delta, 1.2);
804
805 let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.05, 0.11, &0.01);
806 assert_eq!(status, CutStatus::Success);
807 assert_approx_eq!(sigma, 0.8);
808 assert_approx_eq!(rho, 0.06);
809 assert_approx_eq!(delta, 0.8);
810
811 let (status, _result) = ell_calc.calc_parallel_q(-0.07, 0.07, &0.01);
812 assert_eq!(status, CutStatus::NoEffect);
813
814 let (status, (rho, sigma, delta)) = ell_calc.calc_parallel_deep_cut(0.01, 0.04, &0.01);
815 assert_eq!(status, CutStatus::Success);
816 assert_approx_eq!(sigma, 0.928);
817 assert_approx_eq!(rho, 0.0232);
818 assert_approx_eq!(delta, 1.232);
819
820 let (status, _result) = ell_calc.calc_parallel_q(-0.04, 0.0625, &0.01);
821 assert_eq!(status, CutStatus::NoEffect);
822 }
823}