std_dev/
regression.rs

1//! Various regression models to fit the best line to your data.
2//! All written to be understandable.
3//!
4//! Vocabulary:
5//!
6//! - Predictors - the independent values (usually denoted `x`) from which we want a equation to get the:
7//! - outcomes - the dependant variables. Usually `y` or `f(x)`.
8//! - model - create an equation which optimally (can optimize for different priorities) fits the data.
9//!
10//! The `*Coefficients` structs implement [`Predictive`] which calculates the [predicted outcomes](Predictive::predict_outcome)
11//! using the model and their [determination](Determination::determination); and [`Display`] which can be used to
12//! show the equations.
13//!
14//! Linear regressions are often used by other regression methods. All linear regressions therefore
15//! implement the [`LinearEstimator`] trait. You can use the `*Linear` structs to choose which method to
16//! use.
17//!
18//! # Info on implementation
19//!
20//! Details and comments on implementation can be found as docs under each item.
21//!
22//! ## Power & exponent
23//!
24//! See [`derived`] for the implementations.
25//!
26//! I reverse the exponentiation to get a linear model. Then, I solve it using the method linked
27//! above. Then, I transform the returned variables to fit the target model.
28//!
29//! This is not very good, as the errors of large values are reduced compared to small values when
30//! taking the logarithm. I have plans to address this bias in the future.
31//! The current behaviour is however still probably the desired behaviour, as small values are
32//! often relatively important to larger.
33//!
34//! Many programs (including LibreOffice Calc) simply discards negative & zero values. I chose to
35//! go the explicit route and add additional terms to satisfy requirements.
36//! This is naturally a fallback, and should be a warning sign your data is bad.
37//!
38//! Under these methods the calculations are inserted, and how to handle the data.
39#![deny(missing_docs)]
40
41use std::fmt::{self, Display};
42use std::ops::Deref;
43
44#[doc(inline)]
45pub use models::*;
46
47pub use binary_search::Options as BinarySearchOptions;
48#[cfg(feature = "ols")]
49pub use derived::{exponential_ols, power_ols};
50pub use gradient_descent::{
51    ParallelOptions as GradientDescentParallelOptions,
52    SimultaneousOptions as GradientDescentSimultaneousOptions,
53};
54#[cfg(feature = "ols")]
55pub use ols::OlsEstimator;
56pub use spiral::{SpiralLinear, SpiralLogisticWithCeiling};
57pub use theil_sen::{LinearTheilSen, PolynomialTheilSen};
58
59trait Model: Predictive + Display {}
60impl<T: Predictive + Display> Model for T {}
61
62/// Generic model. This enables easily handling results from several models.
63pub struct DynModel {
64    model: Box<dyn Model>,
65}
66impl DynModel {
67    /// Wrap `model` in a [`Box`].
68    pub fn new(model: impl Predictive + Display + 'static) -> Self {
69        Self {
70            model: Box::new(model),
71        }
72    }
73}
74impl Predictive for DynModel {
75    fn predict_outcome(&self, predictor: f64) -> f64 {
76        self.model.predict_outcome(predictor)
77    }
78}
79impl Display for DynModel {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        self.model.fmt(f)
82    }
83}
84
85/// Something that can predict the outcome from a predictor.
86pub trait Predictive {
87    /// Calculates the predicted outcome of `predictor`.
88    fn predict_outcome(&self, predictor: f64) -> f64;
89    /// Put this predicative model in a box.
90    /// This is useful for conditionally choosing different models.
91    fn boxed(self) -> DynModel
92    where
93        Self: Sized + Display + 'static,
94    {
95        DynModel::new(self)
96    }
97}
98impl<T: Predictive + ?Sized> Predictive for &T {
99    fn predict_outcome(&self, predictor: f64) -> f64 {
100        (**self).predict_outcome(predictor)
101    }
102}
103/// Helper trait to make the [R²](Determination::determination) method take a generic iterator.
104///
105/// This enables [`Predictive`] to be `dyn`.
106pub trait Determination: Predictive {
107    /// Calculates the R² (coefficient of determination), the proportion of variation in predicted
108    /// model.
109    ///
110    /// `predictors` are the x values (input to the function).
111    /// `outcomes` are the observed dependant variable.
112    /// `len` is the count of data points.
113    ///
114    /// If `predictors` and `outcomes` have different lengths, the result might be unexpected.
115    ///
116    /// O(n)
117    // For implementation, see https://en.wikipedia.org/wiki/Coefficient_of_determination#Definitions
118    fn determination(
119        &self,
120        predictors: impl Iterator<Item = f64>,
121        outcomes: impl Iterator<Item = f64> + Clone,
122        len: usize,
123    ) -> f64 {
124        let outcomes_mean = outcomes.clone().sum::<f64>() / len as f64;
125        let residuals = predictors
126            .zip(outcomes.clone())
127            .map(|(pred, out)| out - self.predict_outcome(pred));
128
129        // Sum of the square of the residuals
130        let res: f64 = residuals.map(|residual| residual * residual).sum();
131        let tot: f64 = outcomes
132            .map(|out| {
133                let diff = out - outcomes_mean;
134                diff * diff
135            })
136            .sum();
137
138        let mut diff = res / tot;
139
140        if diff.is_nan() {
141            diff = 0.
142        };
143
144        1.0 - diff
145    }
146    /// Convenience method for [`Determination::determination`] when using slices.
147    fn determination_slice(&self, predictors: &[f64], outcomes: &[f64]) -> f64 {
148        assert_eq!(
149            predictors.len(),
150            outcomes.len(),
151            "predictors and outcomes must have the same number of items"
152        );
153        Determination::determination(
154            self,
155            predictors.iter().cloned(),
156            outcomes.iter().cloned(),
157            predictors.len(),
158        )
159    }
160}
161impl<T: Predictive> Determination for T {}
162
163/// The models (functions) we can use regression to optimize for.
164///
165/// You can naturally implement these yourself.
166pub mod models {
167    use super::*;
168    use std::f64::consts::E;
169
170    pub use trig::*;
171
172    macro_rules! estimator {
173        ($(
174            $(#[$docs:meta])*
175            $name:ident -> $item:ty,
176            $($(#[$more_docs:meta])* ($($arg:ident: $ty:ty),*),)?
177            $model:ident, $box:ident
178        )+) => {
179            $(
180            $(#[$docs])*
181            pub trait $name {
182                // #[doc = stringify!("Model the [`", $item, "`] from `predictors` and `outcomes`."]
183                #[doc = "Model the [`"]
184                #[doc = stringify!($item)]
185                #[doc = "`] from `predictors` and `outcomes`."]
186                $($(#[$more_docs])*)?
187                ///
188                /// # Panics
189                ///
190                /// The two slices must have the same length.
191                fn $model(&self, predictors: &[f64], outcomes: &[f64], $($($arg: $ty),*)?) -> $item;
192                /// Put this estimator in a box.
193                /// This is useful for conditionally choosing different estimators.
194                fn $box(self) -> Box<dyn $name>
195                where
196                    Self: Sized + 'static,
197                {
198                    Box::new(self)
199                }
200            }
201            impl<T: $name + ?Sized> $name for &T {
202                fn $model(&self, predictors: &[f64], outcomes: &[f64], $($($arg:$ty),*)?) -> $item {
203                    (**self).$model(predictors, outcomes, $($($arg),*)?)
204                }
205            }
206            )+
207        };
208    }
209
210    /// The coefficients of a line.
211    #[derive(Debug, Clone, Copy, PartialEq)]
212    pub struct LinearCoefficients {
213        /// slope, x coefficient
214        pub k: f64,
215        /// y intersect, additive
216        pub m: f64,
217    }
218    impl Predictive for LinearCoefficients {
219        fn predict_outcome(&self, predictor: f64) -> f64 {
220            self.k * predictor + self.m
221        }
222    }
223    impl Display for LinearCoefficients {
224        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225            let p = f.precision().unwrap_or(5);
226            write!(f, "{:.2$}x + {:.2$}", self.k, self.m, p)
227        }
228    }
229
230    /// The length of the inner vector is `degree + 1`.
231    ///
232    /// The inner list is in order of smallest exponent to largest: `[0, 2, 1]` means `y = 1x² + 2x + 0`.
233    #[derive(Clone, Debug)]
234    pub struct PolynomialCoefficients {
235        pub(crate) coefficients: Vec<f64>,
236    }
237    impl Deref for PolynomialCoefficients {
238        type Target = [f64];
239        fn deref(&self) -> &Self::Target {
240            &self.coefficients
241        }
242    }
243    impl Display for PolynomialCoefficients {
244        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245            let mut first = true;
246            for (degree, mut coefficient) in self.coefficients.iter().copied().enumerate().rev() {
247                if coefficient.abs() < 1e-100 {
248                    continue;
249                }
250                if !first {
251                    if coefficient.is_sign_positive() {
252                        write!(f, " + ")?;
253                    } else {
254                        write!(f, " - ")?;
255                        coefficient = -coefficient;
256                    }
257                }
258
259                let p = f.precision().unwrap_or(5);
260
261                match degree {
262                    0 => write!(f, "{coefficient:.*}", p)?,
263                    1 => write!(f, "{coefficient:.*}x", p)?,
264                    2..=9 => write!(f, "{coefficient:.0$}x^{degree:.0$}", p)?,
265                    _ => write!(f, "{coefficient:.0$}x^{{{degree:.0$}}}", p)?,
266                }
267
268                first = false;
269            }
270            Ok(())
271        }
272    }
273    impl PolynomialCoefficients {
274        #[inline(always)]
275        fn naive_predict(&self, predictor: f64) -> f64 {
276            match self.coefficients.len() {
277                0 => 0.,
278                1 => self.coefficients[0],
279                2 => self.coefficients[1] * predictor + self.coefficients[0],
280                3 => {
281                    self.coefficients[2] * predictor * predictor
282                        + self.coefficients[1] * predictor
283                        + self.coefficients[0]
284                }
285                4 => {
286                    let p2 = predictor * predictor;
287                    self.coefficients[3] * predictor * p2
288                        + self.coefficients[2] * p2
289                        + self.coefficients[1] * predictor
290                        + self.coefficients[0]
291                }
292                _ => {
293                    let mut out = 0.0;
294                    let mut pred = 1.;
295                    for coefficient in self.coefficients.iter().copied() {
296                        out += pred * coefficient;
297                        pred *= predictor;
298                    }
299                    out
300                }
301            }
302        }
303
304        /// Returns the coefficients for the derivative of these coefficients.
305        pub fn derivative(&self) -> Self {
306            let mut coeffs = Vec::with_capacity(self.len().saturating_sub(1));
307            for (idx, coeff) in self.coefficients.iter().enumerate().skip(1) {
308                coeffs.push(*coeff * (idx) as f64);
309            }
310            Self {
311                coefficients: coeffs,
312            }
313        }
314        /// Returns the coefficients for the integral (primitive function) of these coefficients.
315        pub fn integral(&self) -> Self {
316            let mut coeffs = Vec::with_capacity(self.len() + 1);
317            coeffs.push(0.);
318            for (idx, coeff) in self.coefficients.iter().enumerate() {
319                coeffs.push(*coeff / (idx + 1) as f64);
320            }
321            Self {
322                coefficients: coeffs,
323            }
324        }
325    }
326    impl Predictive for PolynomialCoefficients {
327        #[cfg(feature = "arbitrary-precision")]
328        fn predict_outcome(&self, predictor: f64) -> f64 {
329            if self.coefficients.len() < 10 {
330                self.naive_predict(predictor)
331            } else {
332                use rug::ops::PowAssign;
333                use rug::Assign;
334                use std::ops::MulAssign;
335
336                let precision = (64 + self.len() * 2) as u32;
337                // let precision = arbitrary_linear_algebra::HARDCODED_PRECISION;
338                let mut out = rug::Float::with_val(precision, 0.0f64);
339                let original_predictor = predictor;
340                let mut predictor = rug::Float::with_val(precision, predictor);
341                for (degree, coefficient) in self.coefficients.iter().copied().enumerate() {
342                    // assign to never create a new value.
343                    predictor.pow_assign(degree as u32);
344                    predictor.mul_assign(coefficient);
345                    out += &predictor;
346                    predictor.assign(original_predictor)
347                }
348                out.to_f64()
349            }
350        }
351        #[cfg(not(feature = "arbitrary-precision"))]
352        #[inline(always)]
353        fn predict_outcome(&self, predictor: f64) -> f64 {
354            self.naive_predict(predictor)
355        }
356    }
357    /// The coefficients of a power (also called growth) function (`kx^e`).
358    #[derive(Debug, Clone, PartialEq)]
359    pub struct PowerCoefficients {
360        /// Constant
361        pub k: f64,
362        /// exponent
363        pub e: f64,
364        /// If the predictors needs to have an offset applied to remove values under 1.
365        ///
366        /// Defaults to 0.
367        pub predictor_additive: f64,
368        /// If the outcomes needs to have an offset applied to remove values under 1.
369        ///
370        /// Defaults to 0.
371        pub outcome_additive: f64,
372    }
373    impl Predictive for PowerCoefficients {
374        fn predict_outcome(&self, predictor: f64) -> f64 {
375            self.k * (predictor + self.predictor_additive).powf(self.e) - self.outcome_additive
376        }
377    }
378    impl Display for PowerCoefficients {
379        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380            let p = f.precision().unwrap_or(5);
381            write!(
382                f,
383                "{:.3$} * {x}^{:.3$}{}",
384                self.k,
385                self.e,
386                if self.outcome_additive != 0. {
387                    format!(" - {:.1$}", self.outcome_additive, p)
388                } else {
389                    String::new()
390                },
391                p,
392                x = if self.predictor_additive != 0. {
393                    format!("(x + {:.1$})", self.predictor_additive, p)
394                } else {
395                    "x".to_string()
396                },
397            )
398        }
399    }
400    impl From<LinearCoefficients> for PolynomialCoefficients {
401        fn from(coefficients: LinearCoefficients) -> Self {
402            Self {
403                coefficients: vec![coefficients.m, coefficients.k],
404            }
405        }
406    }
407    impl<T: Into<Vec<f64>>> From<T> for PolynomialCoefficients {
408        fn from(t: T) -> Self {
409            Self {
410                coefficients: t.into(),
411            }
412        }
413    }
414
415    /// The coefficients of a exponential function (`kb^x`).
416    #[derive(Debug, Clone, PartialEq)]
417    pub struct ExponentialCoefficients {
418        /// Constant
419        pub k: f64,
420        /// base
421        pub b: f64,
422        /// If the predictors needs to have an offset applied to remove values under 1.
423        ///
424        /// Defaults to 0.
425        pub predictor_additive: f64,
426        /// If the outcomes needs to have an offset applied to remove values under 1.
427        ///
428        /// Defaults to 0.
429        pub outcome_additive: f64,
430    }
431    impl Predictive for ExponentialCoefficients {
432        fn predict_outcome(&self, predictor: f64) -> f64 {
433            self.k * self.b.powf(predictor + self.predictor_additive) - self.outcome_additive
434        }
435    }
436    impl Display for ExponentialCoefficients {
437        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438            let p = f.precision().unwrap_or(5);
439            write!(
440                f,
441                "{:.3$} * {:.3$}^{x}{}",
442                self.k,
443                self.b,
444                if self.outcome_additive != 0. {
445                    format!(" - {:.1$}", self.outcome_additive, p)
446                } else {
447                    String::new()
448                },
449                p,
450                x = if self.predictor_additive != 0. {
451                    format!("(x + {:.1$})", self.predictor_additive, p)
452                } else {
453                    "x".to_string()
454                },
455            )
456        }
457    }
458
459    /// The coefficients of a [logistic function](https://en.wikipedia.org/wiki/Logistic_function).
460    #[derive(Debug, Clone, Copy, PartialEq)]
461    pub struct LogisticCoefficients {
462        /// The x value of the curve's midpoint
463        pub x0: f64,
464        /// The curve's maximum value
465        pub l: f64,
466        /// The logistic growth rate or steepness of the curve
467        pub k: f64,
468    }
469    impl Predictive for LogisticCoefficients {
470        #[inline(always)]
471        fn predict_outcome(&self, predictor: f64) -> f64 {
472            self.l / (1. + E.powf(-self.k * (predictor - self.x0)))
473        }
474    }
475    impl Display for LogisticCoefficients {
476        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477            let p = f.precision().unwrap_or(5);
478            write!(
479                f,
480                "{:.3$} / (1 + e^({}(x {})))",
481                self.l,
482                if self.k.is_sign_negative() {
483                    format!("{:.1$}", -self.k, p)
484                } else {
485                    format!("-{:.1$}", self.k, p)
486                },
487                if self.x0.is_sign_negative() {
488                    format!("+ {:.1$}", -self.x0, p)
489                } else {
490                    format!("- {:.1$}", self.x0, p)
491                },
492                p
493            )
494        }
495    }
496
497    estimator!(
498        /// Implemented by all estimators yielding a linear 2 variable regression (a line).
499        LinearEstimator -> LinearCoefficients, model_linear, boxed_linear
500
501        /// Implemented by all estimators yielding a polynomial regression.
502        PolynomialEstimator -> PolynomialCoefficients,
503        /// Also takes a `degree` of the target polynomial. Some estimators may panic when `degree`
504        /// is out of their range.
505        (degree: usize), model_polynomial, boxed_polynomial
506
507        /// Implemented by all estimators yielding a power regression.
508        PowerEstimator -> PowerCoefficients, model_power, boxed_power
509
510        /// Implemented by all estimators yielding an exponential regression.
511        ExponentialEstimator -> ExponentialCoefficients, model_exponential, boxed_exponential
512
513        /// Implemented by all estimators yielding an logistic regression.
514        LogisticEstimator -> LogisticCoefficients, model_logistic, boxed_logistic
515    );
516
517    /// Traits and coefficients of trigonometric functions.
518    pub mod trig {
519        use super::*;
520
521        macro_rules! simple_coefficients {
522            ($(
523                $(#[$docs:meta])+
524                $name:ident, f64::$fn:ident
525            )+) => {
526                simple_coefficients!($($(#[$docs])* $name, v f64::$fn(v), stringify!($fn))+);
527            };
528            ($(
529                $(#[$docs:meta])+
530                $name:ident, 1 / f64::$fn:ident, $disp:expr
531            )+) => {
532                simple_coefficients!($($(#[$docs])* $name, v 1.0/f64::$fn(v), $disp)+);
533            };
534            ($(
535                $(#[$docs:meta])+
536                $name:ident, $v:ident $fn:expr, $disp:expr
537            )+) => {
538                $(
539                $(#[$docs])+
540                #[derive(PartialEq, Clone, Debug)]
541                pub struct $name {
542                    /// The amplitude of this function.
543                    pub amplitude: f64,
544                    /// The frequency of this function.
545                    pub frequency: f64,
546                    /// The phase of this function (x offset).
547                    pub phase: f64,
548                }
549                impl Predictive for $name {
550                    fn predict_outcome(&self, predictor: f64) -> f64 {
551                        let $v = predictor * self.frequency + self.phase;
552                        self.amplitude * $fn
553                    }
554                }
555                impl Display for $name {
556                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557                        let p = f.precision().unwrap_or(5);
558                        write!(
559                            f,
560                            "{:.4$}{}({:.4$}x+{:.4$})",
561                            self.amplitude,
562                            $disp,
563                            self.frequency,
564                            self.phase,
565                            p,
566                        )
567                    }
568                }
569                impl $name {
570                    #[inline(always)]
571                    pub(crate) fn wrap(array: [f64; 3]) -> Self {
572                        Self {
573                            amplitude: array[0],
574                            frequency: array[1],
575                            phase: array[2] % (std::f64::consts::PI * 2.),
576                        }
577                    }
578                }
579                )+
580            };
581        }
582        simple_coefficients!(
583            /// The coefficients of a sine wave.
584            SineCoefficients, f64::sin
585            /// The coefficients of a cosine wave.
586            CosineCoefficients, f64::cos
587            /// The coefficients of a tangent function.
588            TangentCoefficients, f64::tan
589        );
590        simple_coefficients!(
591            /// The coefficients of a secant function.
592            SecantCoefficients,
593            1 / f64::sin, "sec"
594            /// The coefficients of a cosecant function.
595            CosecantCoefficients,
596            1 / f64::cos, "csc"
597            /// The coefficients of a cotangent function.
598            CotangentCoefficients,
599            1 / f64::tan, "cot"
600        );
601
602        estimator!(
603            /// Implemented by all estimators yielding a sine wave.
604            SineEstimator -> SineCoefficients, (max_frequency: f64), model_sine, boxed_sine
605            /// Implemented by all estimators yielding a cosine wave.
606            CosineEstimator -> CosineCoefficients, (max_frequency: f64), model_cosine, boxed_cosine
607            /// Implemented by all estimators yielding a tangent function.
608            TangentEstimator -> TangentCoefficients, (max_frequency: f64), model_tangent, boxed_tangent
609
610            /// Implemented by all estimators yielding a secant function.
611            SecantEstimator -> SecantCoefficients, (max_frequency: f64), model_secant, boxed_sesecant
612            /// Implemented by all estimators yielding a cosecant function.
613            CosecantEstimator -> CosecantCoefficients, (max_frequency: f64), model_cosecant, boxed_cosecant
614            /// Implemented by all estimators yielding a cotangent function.
615            CotangentEstimator -> CotangentCoefficients, (max_frequency: f64), model_cotangent, boxed_cotangent
616        );
617    }
618}
619
620/// Finds the model best fit to the input data.
621/// This is done using heuristics and testing of methods.
622///
623/// # Panics
624///
625/// Panics if the model has less than two parameters or if the two slices have different lengths.
626///
627/// # Heuristics
628///
629/// These seemed good to me. Any ideas on improving them are welcome.
630///
631/// - Power and exponentials only if no data is < 1.
632///   This is due to the sub-optimal behaviour of logarithm with values close to and under 0.
633///   This restriction might be lifted to just < 1e-9 in the future.
634/// - Power is heavily favoured if `let distance_from_integer = -(0.5 - exponent % 1).abs() + 0.5;
635///   distance_from_integer < 0.15 && -2.5 <= exponent <= 3.5`
636/// - Power is also heavily favoured if the same as above occurs but with the reciprocal of the
637///   exponent. Then, the range 0.5 < exponent.recip() <= 3.5 is considered.
638/// - Exponential favoured if R² > 0.8, which seldom happens with exponential regression.
639/// - Bump the rating of linear, as that's probably what you want.
640/// - 2'nd degree polynomial is only considered if `n > 15`, where `n` is `predictors.len()`.
641/// - 3'nd degree polynomial is only considered if `n > 50`
642pub fn best_fit(
643    predictors: &[f64],
644    outcomes: &[f64],
645    linear_estimator: &impl LinearEstimator,
646) -> DynModel {
647    // These values are chosen from heuristics in my brain
648    /// Additive
649    const LINEAR_BUMP: f64 = 0.0;
650    /// Multiplicative
651    const POWER_BUMP: f64 = 1.5;
652    /// Multiplicative
653    const EXPONENTIAL_BUMP: f64 = 1.3;
654    /// Used to partially mitigate [overfitting](https://en.wikipedia.org/wiki/Overfitting).
655    ///
656    /// Multiplicative
657    // `TODO`: remove when we use generic polynomial provider
658    #[allow(unused)]
659    const SECOND_DEGREE_DISADVANTAGE: f64 = 0.94;
660    /// Used to partially mitigate [overfitting](https://en.wikipedia.org/wiki/Overfitting).
661    ///
662    /// Multiplicative
663    // `TODO`: remove when we use generic polynomial provider
664    #[allow(unused)]
665    const THIRD_DEGREE_DISADVANTAGE: f64 = 0.9;
666
667    let mut best: Option<(DynModel, f64)> = None;
668    macro_rules! update_best {
669        ($new: expr, $e: ident, $modificator: expr, $err: expr) => {
670            let $e = $err;
671            let weighted = $modificator;
672            if let Some((_, error)) = &best {
673                if weighted > *error {
674                    best = Some((DynModel::new($new), weighted))
675                }
676            } else {
677                best = Some((DynModel::new($new), weighted))
678            }
679        };
680        ($new: expr, $e: ident, $modificator: expr) => {
681            update_best!(
682                $new,
683                $e,
684                $modificator,
685                $new.determination_slice(predictors, outcomes)
686            )
687        };
688        ($new: expr) => {
689            update_best!($new, e, e)
690        };
691    }
692
693    let predictor_min = derived::min(predictors).unwrap();
694    let outcomes_min = derived::min(outcomes).unwrap();
695
696    if predictor_min >= 1.0 && outcomes_min >= 1.0 {
697        let mut mod_predictors = predictors.to_vec();
698        let mut mod_outcomes = outcomes.to_vec();
699        let power = derived::power_given_min(
700            &mut mod_predictors,
701            &mut mod_outcomes,
702            predictor_min,
703            outcomes_min,
704            linear_estimator,
705        );
706
707        let distance_from_integer = -(0.5 - power.e % 1.0).abs() + 0.5;
708        let mut power_bump = 1.0;
709        if distance_from_integer < 0.15 && power.e <= 3.5 && power.e >= -2.5 {
710            power_bump *= POWER_BUMP;
711        }
712        let distance_from_fraction = -(0.5 - power.e.recip() % 1.0).abs() + 0.5;
713        if distance_from_fraction < 0.1 && power.e.recip() <= 3.5 && power.e.recip() > 0.5 {
714            power_bump *= POWER_BUMP;
715        }
716        let certainty = power.determination_slice(predictors, outcomes);
717        if certainty > 0.8 {
718            power_bump *= EXPONENTIAL_BUMP;
719        }
720        if certainty > 0.92 {
721            power_bump *= EXPONENTIAL_BUMP;
722        }
723
724        update_best!(power, e, e * power_bump, certainty);
725
726        mod_predictors[..].copy_from_slice(predictors);
727        mod_outcomes[..].copy_from_slice(outcomes);
728
729        let exponential = derived::exponential_given_min(
730            &mut mod_predictors,
731            &mut mod_outcomes,
732            predictor_min,
733            outcomes_min,
734            linear_estimator,
735        );
736        let certainty = exponential.determination_slice(predictors, outcomes);
737
738        let mut exponential_bump = if certainty > 0.8 {
739            EXPONENTIAL_BUMP
740        } else {
741            1.0
742        };
743        if certainty > 0.92 {
744            exponential_bump *= EXPONENTIAL_BUMP;
745        }
746
747        update_best!(exponential, e, e * exponential_bump, certainty);
748    }
749    // `TODO`: use generic polynomial provider.
750    #[cfg(feature = "ols")]
751    if predictors.len() > 15 {
752        let degree_2 = ols::polynomial(
753            predictors.iter().copied(),
754            outcomes.iter().copied(),
755            predictors.len(),
756            2,
757        );
758
759        update_best!(degree_2, e, e * SECOND_DEGREE_DISADVANTAGE);
760    }
761    #[cfg(feature = "ols")]
762    if predictors.len() > 50 {
763        let degree_3 = ols::polynomial(
764            predictors.iter().copied(),
765            outcomes.iter().copied(),
766            predictors.len(),
767            3,
768        );
769
770        update_best!(degree_3, e, e * THIRD_DEGREE_DISADVANTAGE);
771    }
772
773    let linear = linear_estimator.model_linear(predictors, outcomes);
774    update_best!(linear, e, e + LINEAR_BUMP);
775    // UNWRAP: We just set it, at least there's a linear.
776    best.unwrap().0
777}
778/// Convenience function for [`best_fit`] using [`OlsEstimator`].
779#[cfg(feature = "ols")]
780pub fn best_fit_ols(predictors: &[f64], outcomes: &[f64]) -> DynModel {
781    best_fit(predictors, outcomes, &OlsEstimator)
782}
783
784/// Estimators derived from others, usual [`LinearEstimator`].
785///
786/// These do not (for now) implement [`PowerEstimator`] nor [`ExponentialEstimator`]
787/// because of the requirement of mutable slices instead of immutable ones.
788///
789/// See the docs on the items for more info about how they're created.
790pub mod derived {
791    use super::*;
792    pub(super) fn min(slice: &[f64]) -> Option<f64> {
793        slice
794            .iter()
795            .copied()
796            .map(crate::F64OrdHash)
797            .min()
798            .map(|f| f.0)
799    }
800
801    /// Convenience-method for [`power`] using [`OlsEstimator`].
802    #[cfg(feature = "ols")]
803    pub fn power_ols(predictors: &mut [f64], outcomes: &mut [f64]) -> PowerCoefficients {
804        power(predictors, outcomes, &OlsEstimator)
805    }
806    /// Fits a curve with the equation `y = a * x^b` (optionally with an additional subtractive term if
807    /// any outcome is < 1 and an additive to the `x` if any predictor is < 1).
808    ///
809    /// Also sometimes called "growth".
810    ///
811    /// # Panics
812    ///
813    /// Panics if either `x` or `y` don't have the length `len`.
814    /// `len` must be greater than 2.
815    ///
816    /// # Derivation
817    ///
818    /// y=b * x^a
819    ///
820    /// lg(y) = lg(b * x^a)
821    /// lg(y) = lg(b) + a(lg x)
822    ///
823    /// Transform: y => lg (y), x => lg(x)
824    ///
825    /// When values found, take 10^b to get b and a is a
826    pub fn power<E: LinearEstimator>(
827        predictors: &mut [f64],
828        outcomes: &mut [f64],
829        estimator: &E,
830    ) -> PowerCoefficients {
831        assert!(predictors.len() > 2);
832        assert!(outcomes.len() > 2);
833        let predictor_min = min(predictors).unwrap();
834        let outcome_min = min(outcomes).unwrap();
835        power_given_min(predictors, outcomes, predictor_min, outcome_min, estimator)
836    }
837    /// Same as [`power`] without the [`Clone`] requirement for the iterators, but takes a min
838    /// value.
839    ///
840    /// # Panics
841    ///
842    /// See [`power`].
843    pub fn power_given_min<E: LinearEstimator>(
844        predictors: &mut [f64],
845        outcomes: &mut [f64],
846        predictor_min: f64,
847        outcome_min: f64,
848        estimator: &E,
849    ) -> PowerCoefficients {
850        assert_eq!(predictors.len(), outcomes.len());
851        assert!(predictors.len() > 2);
852
853        // If less than 1, exception. Read more about this in the `power` function docs.
854        let predictor_additive = if predictor_min < 1.0 {
855            Some(1.0 - predictor_min)
856        } else {
857            None
858        };
859        let outcome_additive = if outcome_min < 1.0 {
860            Some(1.0 - outcome_min)
861        } else {
862            None
863        };
864
865        predictors
866            .iter_mut()
867            .for_each(|pred| *pred = (*pred + predictor_additive.unwrap_or(0.0)).log2());
868        outcomes
869            .iter_mut()
870            .for_each(|y| *y = (*y + outcome_additive.unwrap_or(0.0)).log2());
871
872        let coefficients = estimator.model_linear(predictors, outcomes);
873        let k = 2.0_f64.powf(coefficients.m);
874        let e = coefficients.k;
875        PowerCoefficients {
876            k,
877            e,
878            predictor_additive: predictor_additive.unwrap_or(0.),
879            outcome_additive: outcome_additive.unwrap_or(0.),
880        }
881    }
882
883    /// Convenience-method for [`exponential`] using [`OlsEstimator`].
884    #[cfg(feature = "ols")]
885    pub fn exponential_ols(
886        predictors: &mut [f64],
887        outcomes: &mut [f64],
888    ) -> ExponentialCoefficients {
889        exponential(predictors, outcomes, &OlsEstimator)
890    }
891    /// Fits a curve with the equation `y = a * b^x` (optionally with an additional subtractive term if
892    /// any outcome is < 1 and an additive to the `x` if any predictor is < 1).
893    ///
894    /// # Panics
895    ///
896    /// Panics if either `x` or `y` don't have the length `len`.
897    /// `len` must be greater than 2.
898    ///
899    /// # Derivation
900    ///
901    /// y=b * a^x
902    ///
903    /// lg(y) = lg(b * a^x)
904    /// lg(y) = lg(b) + x(lg a)
905    ///
906    /// Transform: y => lg (y), x => x
907    ///
908    /// When values found, take 10^b to get b and 10^a to get a
909    pub fn exponential<E: LinearEstimator>(
910        predictors: &mut [f64],
911        outcomes: &mut [f64],
912        estimator: &E,
913    ) -> ExponentialCoefficients {
914        assert!(predictors.len() > 2);
915        assert!(outcomes.len() > 2);
916        let predictor_min = min(predictors).unwrap();
917        let outcome_min = min(outcomes).unwrap();
918        exponential_given_min(predictors, outcomes, predictor_min, outcome_min, estimator)
919    }
920    /// Same as [`exponential`] without the [`Clone`] requirement for the iterators, but takes a min
921    /// value.
922    ///
923    /// # Panics
924    ///
925    /// See [`exponential`].
926    pub fn exponential_given_min<E: LinearEstimator>(
927        predictors: &mut [f64],
928        outcomes: &mut [f64],
929        predictor_min: f64,
930        outcome_min: f64,
931        estimator: &E,
932    ) -> ExponentialCoefficients {
933        assert_eq!(predictors.len(), outcomes.len());
934        assert!(predictors.len() > 2);
935
936        // If less than 1, exception. Read more about this in the `exponential` function docs.
937        let predictor_additive = if predictor_min < 1.0 {
938            Some(1.0 - predictor_min)
939        } else {
940            None
941        };
942        let outcome_additive = if outcome_min < 1.0 {
943            Some(1.0 - outcome_min)
944        } else {
945            None
946        };
947
948        if let Some(predictor_additive) = predictor_additive {
949            predictors
950                .iter_mut()
951                .for_each(|pred| *pred += predictor_additive);
952        }
953        outcomes
954            .iter_mut()
955            .for_each(|y| *y = (*y + outcome_additive.unwrap_or(0.0)).log2());
956
957        let coefficients = estimator.model_linear(predictors, outcomes);
958        let k = 2.0_f64.powf(coefficients.m);
959        let b = 2.0_f64.powf(coefficients.k);
960        ExponentialCoefficients {
961            k,
962            b,
963            predictor_additive: predictor_additive.unwrap_or(0.),
964            outcome_additive: outcome_additive.unwrap_or(0.),
965        }
966    }
967}
968
969/// This module enables the use of [`rug::Float`] inside of [`nalgebra`].
970///
971/// Many functions are not implemented. PRs are welcome.
972#[cfg(feature = "arbitrary-precision")]
973pub mod arbitrary_linear_algebra {
974    use std::cell::RefCell;
975    use std::fmt::{self, Display};
976    use std::ops::{
977        Add, AddAssign, Deref, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
978    };
979
980    use nalgebra::{ComplexField, RealField};
981    use rug::Assign;
982
983    thread_local! {
984        /// The default precision.
985        ///
986        /// This is thread-local.
987        pub static DEFAULT_PRECISION: RefCell<u32> = RefCell::new(256);
988    }
989    /// Set the default precision **for this thread**.
990    pub fn set_default_precision(new: u32) {
991        DEFAULT_PRECISION.with(|v| *v.borrow_mut() = new);
992    }
993    /// Get the default precision.
994    /// Can be set using [`set_default_precision`].
995    pub fn default_precision() -> u32 {
996        DEFAULT_PRECISION.with(|v| *v.borrow())
997    }
998    /// A wrapper around [`rug::Float`] to implement traits for.
999    #[derive(Debug, Clone, PartialEq, PartialOrd)]
1000    pub struct FloatWrapper(pub rug::Float);
1001    impl From<rug::Float> for FloatWrapper {
1002        fn from(f: rug::Float) -> Self {
1003            Self(f)
1004        }
1005    }
1006
1007    impl simba::scalar::SupersetOf<f64> for FloatWrapper {
1008        fn is_in_subset(&self) -> bool {
1009            self.0.prec() <= 53
1010        }
1011        fn to_subset(&self) -> Option<f64> {
1012            if simba::scalar::SupersetOf::<f64>::is_in_subset(self) {
1013                Some(self.0.to_f64())
1014            } else {
1015                None
1016            }
1017        }
1018        fn to_subset_unchecked(&self) -> f64 {
1019            self.0.to_f64()
1020        }
1021        fn from_subset(element: &f64) -> Self {
1022            rug::Float::with_val(default_precision(), element).into()
1023        }
1024    }
1025    impl simba::scalar::SubsetOf<Self> for FloatWrapper {
1026        fn to_superset(&self) -> Self {
1027            self.clone()
1028        }
1029
1030        fn from_superset_unchecked(element: &Self) -> Self {
1031            element.clone()
1032        }
1033
1034        fn is_in_subset(_element: &Self) -> bool {
1035            true
1036        }
1037    }
1038    impl num_traits::cast::FromPrimitive for FloatWrapper {
1039        fn from_i64(n: i64) -> Option<Self> {
1040            Some(rug::Float::with_val(default_precision(), n).into())
1041        }
1042        fn from_u64(n: u64) -> Option<Self> {
1043            Some(rug::Float::with_val(default_precision(), n).into())
1044        }
1045    }
1046    impl Display for FloatWrapper {
1047        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048            self.0.fmt(f)
1049        }
1050    }
1051    impl simba::simd::SimdValue for FloatWrapper {
1052        type Element = FloatWrapper;
1053        type SimdBool = bool;
1054
1055        #[inline(always)]
1056        fn lanes() -> usize {
1057            1
1058        }
1059
1060        #[inline(always)]
1061        fn splat(val: Self::Element) -> Self {
1062            val
1063        }
1064
1065        #[inline(always)]
1066        fn extract(&self, _: usize) -> Self::Element {
1067            self.clone()
1068        }
1069
1070        #[inline(always)]
1071        unsafe fn extract_unchecked(&self, _: usize) -> Self::Element {
1072            self.clone()
1073        }
1074
1075        #[inline(always)]
1076        fn replace(&mut self, _: usize, val: Self::Element) {
1077            *self = val
1078        }
1079
1080        #[inline(always)]
1081        unsafe fn replace_unchecked(&mut self, _: usize, val: Self::Element) {
1082            *self = val
1083        }
1084
1085        #[inline(always)]
1086        fn select(self, cond: Self::SimdBool, other: Self) -> Self {
1087            if cond {
1088                self
1089            } else {
1090                other
1091            }
1092        }
1093    }
1094    impl Neg for FloatWrapper {
1095        type Output = Self;
1096        fn neg(self) -> Self::Output {
1097            Self(-self.0)
1098        }
1099    }
1100    impl Add for FloatWrapper {
1101        type Output = Self;
1102        fn add(mut self, rhs: Self) -> Self::Output {
1103            self.0 += rhs.0;
1104            self
1105        }
1106    }
1107    impl Sub for FloatWrapper {
1108        type Output = Self;
1109        fn sub(mut self, rhs: Self) -> Self::Output {
1110            self.0 -= rhs.0;
1111            self
1112        }
1113    }
1114    impl Mul for FloatWrapper {
1115        type Output = Self;
1116        fn mul(mut self, rhs: Self) -> Self::Output {
1117            self.0 *= rhs.0;
1118            self
1119        }
1120    }
1121    impl Div for FloatWrapper {
1122        type Output = Self;
1123        fn div(mut self, rhs: Self) -> Self::Output {
1124            self.0 /= rhs.0;
1125            self
1126        }
1127    }
1128    impl Rem for FloatWrapper {
1129        type Output = Self;
1130        fn rem(mut self, rhs: Self) -> Self::Output {
1131            self.0 %= rhs.0;
1132            self
1133        }
1134    }
1135    impl AddAssign for FloatWrapper {
1136        fn add_assign(&mut self, rhs: Self) {
1137            self.0 += rhs.0;
1138        }
1139    }
1140    impl SubAssign for FloatWrapper {
1141        fn sub_assign(&mut self, rhs: Self) {
1142            self.0 -= rhs.0;
1143        }
1144    }
1145    impl MulAssign for FloatWrapper {
1146        fn mul_assign(&mut self, rhs: Self) {
1147            self.0 *= rhs.0;
1148        }
1149    }
1150    impl DivAssign for FloatWrapper {
1151        fn div_assign(&mut self, rhs: Self) {
1152            self.0 /= rhs.0;
1153        }
1154    }
1155    impl RemAssign for FloatWrapper {
1156        fn rem_assign(&mut self, rhs: Self) {
1157            self.0 %= rhs.0;
1158        }
1159    }
1160    impl num_traits::Zero for FloatWrapper {
1161        fn zero() -> Self {
1162            Self(rug::Float::with_val(default_precision(), 0.0))
1163        }
1164        fn is_zero(&self) -> bool {
1165            self.0 == 0.0
1166        }
1167    }
1168    impl num_traits::One for FloatWrapper {
1169        fn one() -> Self {
1170            Self(rug::Float::with_val(default_precision(), 1.0))
1171        }
1172    }
1173    impl num_traits::Num for FloatWrapper {
1174        type FromStrRadixErr = rug::float::ParseFloatError;
1175        fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
1176            rug::Float::parse_radix(s, radix as i32)
1177                .map(|f| Self(rug::Float::with_val(default_precision(), f)))
1178        }
1179    }
1180    impl num_traits::Signed for FloatWrapper {
1181        fn abs(&self) -> Self {
1182            (*self.0.as_abs()).clone().into()
1183        }
1184        fn abs_sub(&self, other: &Self) -> Self {
1185            if self.0 <= other.0 {
1186                rug::Float::with_val(self.prec(), 0.0f64).into()
1187            } else {
1188                Self(self.0.clone() - &other.0)
1189            }
1190        }
1191        fn signum(&self) -> Self {
1192            self.0.clone().signum().into()
1193        }
1194        fn is_positive(&self) -> bool {
1195            self.0.is_sign_positive()
1196        }
1197        fn is_negative(&self) -> bool {
1198            self.0.is_sign_negative()
1199        }
1200    }
1201    impl approx::AbsDiffEq for FloatWrapper {
1202        type Epsilon = Self;
1203        fn default_epsilon() -> Self::Epsilon {
1204            rug::Float::with_val(default_precision(), f64::EPSILON).into()
1205        }
1206        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
1207            if self.0 == other.0 {
1208                return true;
1209            }
1210            if self.0.is_infinite() || other.0.is_infinite() {
1211                return false;
1212            }
1213            let mut buffer = self.clone();
1214            buffer.0.assign(&self.0 - &other.0);
1215            buffer.0.abs_mut();
1216            let abs_diff = buffer;
1217            abs_diff.0 <= epsilon.0
1218        }
1219    }
1220    impl approx::RelativeEq for FloatWrapper {
1221        fn default_max_relative() -> Self::Epsilon {
1222            rug::Float::with_val(default_precision(), f64::EPSILON).into()
1223        }
1224        fn relative_eq(
1225            &self,
1226            other: &Self,
1227            epsilon: Self::Epsilon,
1228            max_relative: Self::Epsilon,
1229        ) -> bool {
1230            if self.0 == other.0 {
1231                return true;
1232            }
1233            if self.0.is_infinite() || other.0.is_infinite() {
1234                return false;
1235            }
1236            let mut buffer = self.clone();
1237            buffer.0.assign(&self.0 - &other.0);
1238            buffer.0.abs_mut();
1239            let abs_diff = buffer;
1240            if abs_diff.0 <= epsilon.0 {
1241                return true;
1242            }
1243
1244            let abs_self = self.0.as_abs();
1245            let abs_other = other.0.as_abs();
1246
1247            let largest = if *abs_other > *abs_self {
1248                &*abs_other
1249            } else {
1250                &*abs_self
1251            };
1252
1253            abs_diff.0 <= largest * max_relative.0
1254        }
1255    }
1256    impl approx::UlpsEq for FloatWrapper {
1257        fn default_max_ulps() -> u32 {
1258            // Should not be used, see comment below.
1259            4
1260        }
1261        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, _max_ulps: u32) -> bool {
1262            // taking the difference of the bits makes no sense when using arbitrary floats.
1263            approx::AbsDiffEq::abs_diff_eq(&self, &other, epsilon)
1264        }
1265    }
1266    impl nalgebra::Field for FloatWrapper {}
1267    impl RealField for FloatWrapper {
1268        fn is_sign_positive(&self) -> bool {
1269            todo!()
1270        }
1271
1272        fn is_sign_negative(&self) -> bool {
1273            todo!()
1274        }
1275
1276        fn copysign(self, _sign: Self) -> Self {
1277            todo!()
1278        }
1279
1280        fn max(self, _other: Self) -> Self {
1281            todo!()
1282        }
1283
1284        fn min(self, _other: Self) -> Self {
1285            todo!()
1286        }
1287
1288        fn clamp(self, _min: Self, _max: Self) -> Self {
1289            todo!()
1290        }
1291
1292        fn atan2(self, _other: Self) -> Self {
1293            todo!()
1294        }
1295
1296        fn min_value() -> Option<Self> {
1297            todo!()
1298        }
1299
1300        fn max_value() -> Option<Self> {
1301            todo!()
1302        }
1303
1304        fn pi() -> Self {
1305            todo!()
1306        }
1307
1308        fn two_pi() -> Self {
1309            todo!()
1310        }
1311
1312        fn frac_pi_2() -> Self {
1313            todo!()
1314        }
1315
1316        fn frac_pi_3() -> Self {
1317            todo!()
1318        }
1319
1320        fn frac_pi_4() -> Self {
1321            todo!()
1322        }
1323
1324        fn frac_pi_6() -> Self {
1325            todo!()
1326        }
1327
1328        fn frac_pi_8() -> Self {
1329            todo!()
1330        }
1331
1332        fn frac_1_pi() -> Self {
1333            todo!()
1334        }
1335
1336        fn frac_2_pi() -> Self {
1337            todo!()
1338        }
1339
1340        fn frac_2_sqrt_pi() -> Self {
1341            todo!()
1342        }
1343
1344        fn e() -> Self {
1345            todo!()
1346        }
1347
1348        fn log2_e() -> Self {
1349            todo!()
1350        }
1351
1352        fn log10_e() -> Self {
1353            todo!()
1354        }
1355
1356        fn ln_2() -> Self {
1357            todo!()
1358        }
1359
1360        fn ln_10() -> Self {
1361            todo!()
1362        }
1363    }
1364    impl ComplexField for FloatWrapper {
1365        type RealField = Self;
1366
1367        fn from_real(re: Self::RealField) -> Self {
1368            re
1369        }
1370        fn real(self) -> Self::RealField {
1371            self
1372        }
1373        fn imaginary(mut self) -> Self::RealField {
1374            self.0.assign(0.0);
1375            self
1376        }
1377        fn modulus(self) -> Self::RealField {
1378            self.abs()
1379        }
1380        fn modulus_squared(self) -> Self::RealField {
1381            self.0.square().into()
1382        }
1383        fn argument(mut self) -> Self::RealField {
1384            if self.0.is_sign_positive() || self.0.is_zero() {
1385                self.0.assign(0.0);
1386                self
1387            } else {
1388                Self::pi()
1389            }
1390        }
1391        fn norm1(self) -> Self::RealField {
1392            self.abs()
1393        }
1394        fn scale(self, factor: Self::RealField) -> Self {
1395            self.0.mul(factor.0).into()
1396        }
1397        fn unscale(self, factor: Self::RealField) -> Self {
1398            self.0.div(factor.0).into()
1399        }
1400        fn floor(self) -> Self {
1401            todo!()
1402        }
1403        fn ceil(self) -> Self {
1404            todo!()
1405        }
1406        fn round(self) -> Self {
1407            todo!()
1408        }
1409        fn trunc(self) -> Self {
1410            todo!()
1411        }
1412        fn fract(self) -> Self {
1413            todo!()
1414        }
1415        fn mul_add(self, _a: Self, _b: Self) -> Self {
1416            todo!()
1417        }
1418        fn abs(self) -> Self::RealField {
1419            self.0.abs().into()
1420        }
1421        fn hypot(self, other: Self) -> Self::RealField {
1422            self.0.hypot(&other.0).into()
1423        }
1424        fn recip(self) -> Self {
1425            todo!()
1426        }
1427        fn conjugate(self) -> Self {
1428            self
1429        }
1430        fn sin(self) -> Self {
1431            todo!()
1432        }
1433        fn cos(self) -> Self {
1434            todo!()
1435        }
1436        fn sin_cos(self) -> (Self, Self) {
1437            todo!()
1438        }
1439        fn tan(self) -> Self {
1440            todo!()
1441        }
1442        fn asin(self) -> Self {
1443            todo!()
1444        }
1445        fn acos(self) -> Self {
1446            todo!()
1447        }
1448        fn atan(self) -> Self {
1449            todo!()
1450        }
1451        fn sinh(self) -> Self {
1452            todo!()
1453        }
1454        fn cosh(self) -> Self {
1455            todo!()
1456        }
1457        fn tanh(self) -> Self {
1458            todo!()
1459        }
1460        fn asinh(self) -> Self {
1461            todo!()
1462        }
1463        fn acosh(self) -> Self {
1464            todo!()
1465        }
1466        fn atanh(self) -> Self {
1467            todo!()
1468        }
1469        fn log(self, _base: Self::RealField) -> Self {
1470            todo!()
1471        }
1472        fn log2(self) -> Self {
1473            todo!()
1474        }
1475        fn log10(self) -> Self {
1476            todo!()
1477        }
1478        fn ln(self) -> Self {
1479            todo!()
1480        }
1481        fn ln_1p(self) -> Self {
1482            todo!()
1483        }
1484        fn sqrt(self) -> Self {
1485            self.0.sqrt().into()
1486        }
1487        fn exp(self) -> Self {
1488            todo!()
1489        }
1490        fn exp2(self) -> Self {
1491            todo!()
1492        }
1493        fn exp_m1(self) -> Self {
1494            todo!()
1495        }
1496        fn powi(self, _n: i32) -> Self {
1497            todo!()
1498        }
1499        fn powf(self, _n: Self::RealField) -> Self {
1500            todo!()
1501        }
1502        fn powc(self, _n: Self) -> Self {
1503            todo!()
1504        }
1505        fn cbrt(self) -> Self {
1506            todo!()
1507        }
1508        fn try_sqrt(self) -> Option<Self> {
1509            todo!()
1510        }
1511        fn is_finite(&self) -> bool {
1512            self.0.is_finite()
1513        }
1514    }
1515    impl Deref for FloatWrapper {
1516        type Target = rug::Float;
1517        fn deref(&self) -> &Self::Target {
1518            &self.0
1519        }
1520    }
1521}
1522
1523/// [Ordinary least squares](https://en.wikipedia.org/wiki/Ordinary_least_squares) implementation.
1524///
1525/// # Implementation details
1526///
1527/// This implementation uses linear algebra (namely matrix multiplication, transposed matrices &
1528/// the inverse).
1529/// For now, I'm not educated enough to understand how to derive it.
1530/// I've linked great resources below.
1531///
1532/// The implementation in code should be relatively simple to follow.
1533///
1534/// [Linear regression](https://towardsdatascience.com/implementing-linear-and-polynomial-regression-from-scratch-f1e3d422e6b4)
1535/// [How the linear algebra works](https://medium.com/@andrew.chamberlain/the-linear-algebra-view-of-least-squares-regression-f67044b7f39b)
1536#[cfg(feature = "ols")]
1537pub mod ols {
1538    use std::cell::RefCell;
1539
1540    use nalgebra::DMatrix;
1541
1542    use super::*;
1543
1544    #[must_use]
1545    struct RuntimeMatrices {
1546        design: DMatrix<f64>,
1547        transposed: DMatrix<f64>,
1548        outcomes: DMatrix<f64>,
1549        intermediary1: DMatrix<f64>,
1550        intermediary2: DMatrix<f64>,
1551        result: DMatrix<f64>,
1552
1553        len: usize,
1554        degree: usize,
1555    }
1556    impl RuntimeMatrices {
1557        fn new() -> Self {
1558            Self {
1559                design: DMatrix::zeros(0, 0),
1560                transposed: DMatrix::zeros(0, 0),
1561                outcomes: DMatrix::zeros(0, 0),
1562                intermediary1: DMatrix::zeros(0, 0),
1563                intermediary2: DMatrix::zeros(0, 0),
1564                result: DMatrix::zeros(0, 0),
1565
1566                len: 0,
1567                degree: 0,
1568            }
1569        }
1570        /// No guarantees are made to the content of the matrix.
1571        #[inline]
1572        fn resize(&mut self, len: usize, degree: usize) {
1573            if self.len == len && self.degree == degree {
1574                return;
1575            }
1576            let rows = len;
1577            let columns = degree + 1;
1578            self.design.resize_mut(rows, columns, 0.);
1579            self.transposed.resize_mut(columns, rows, 0.);
1580            self.outcomes.resize_mut(rows, 1, 0.);
1581            self.intermediary1.resize_mut(columns, columns, 0.);
1582            self.intermediary2.resize_mut(rows, columns, 0.);
1583            self.result.resize_mut(columns, 1, 0.);
1584            self.len = len;
1585            self.degree = degree;
1586        }
1587    }
1588    thread_local! {static RUNTIME: RefCell<RuntimeMatrices> = RefCell::new(RuntimeMatrices::new());}
1589
1590    /// Linear: `O(n)`
1591    /// Polynomial: `O(n*degree)`, which when using a set `degree` becomes `O(n)`
1592    pub struct OlsEstimator;
1593    impl LinearEstimator for OlsEstimator {
1594        fn model_linear(&self, predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
1595            let coefficients = polynomial(
1596                predictors.iter().copied(),
1597                outcomes.iter().copied(),
1598                predictors.len(),
1599                1,
1600            );
1601            LinearCoefficients {
1602                k: coefficients[1],
1603                m: coefficients[0],
1604            }
1605        }
1606    }
1607    impl PolynomialEstimator for OlsEstimator {
1608        fn model_polynomial(
1609            &self,
1610            predictors: &[f64],
1611            outcomes: &[f64],
1612            degree: usize,
1613        ) -> PolynomialCoefficients {
1614            assert_eq!(predictors.len(), outcomes.len());
1615            polynomial(
1616                predictors.iter().copied(),
1617                outcomes.iter().copied(),
1618                predictors.len(),
1619                degree,
1620            )
1621        }
1622    }
1623
1624    /// # Panics
1625    ///
1626    /// Panics if either `x` or `y` don't have the length `len`.
1627    ///
1628    /// Also panics if `degree + 1 > len`.
1629    #[inline(always)]
1630    pub fn polynomial(
1631        predictors: impl Iterator<Item = f64> + Clone,
1632        outcomes: impl Iterator<Item = f64>,
1633        len: usize,
1634        degree: usize,
1635    ) -> PolynomialCoefficients {
1636        // this is the same as [`polynomial_simple_preallocated`], but clearer for the reader
1637        #[allow(unused)]
1638        fn polynomial_simple(
1639            predictors: impl Iterator<Item = f64> + Clone,
1640            outcomes: impl Iterator<Item = f64>,
1641            len: usize,
1642            degree: usize,
1643        ) -> PolynomialCoefficients {
1644            let predictor_original = predictors.clone();
1645            let mut predictor_iter = predictors;
1646
1647            let design =
1648                nalgebra::DMatrix::from_fn(len, degree + 1, |row: usize, column: usize| {
1649                    if column == 0 {
1650                        1.0
1651                    } else if column == 1 {
1652                        predictor_iter.next().unwrap()
1653                    } else {
1654                        if row == 0 {
1655                            predictor_iter = predictor_original.clone();
1656                        }
1657                        predictor_iter.next().unwrap().powi(column as _)
1658                    }
1659                });
1660
1661            let t = design.transpose();
1662            let outcomes = nalgebra::DMatrix::from_iterator(len, 1, outcomes);
1663            let result = ((&t * &design)
1664                .try_inverse()
1665                .unwrap_or_else(|| (&t * &design).pseudo_inverse(0e-6).unwrap())
1666                * &t)
1667                * outcomes;
1668
1669            PolynomialCoefficients {
1670                coefficients: result.iter().copied().collect(),
1671            }
1672        }
1673        // like [`polynomial_simple`], but with persistent allocations.
1674        fn polynomial_simple_preallocated(
1675            predictors: impl Iterator<Item = f64> + Clone,
1676            outcomes: impl Iterator<Item = f64>,
1677            len: usize,
1678            degree: usize,
1679        ) -> PolynomialCoefficients {
1680            RUNTIME.with(move |runtime| {
1681                let mut runtime = runtime.borrow_mut();
1682                // cheap clone call, it's an iterator
1683                let predictor_original = predictors.clone();
1684                let mut predictor_iter = predictors;
1685
1686                runtime.resize(len, degree);
1687
1688                let RuntimeMatrices {
1689                    design,
1690                    transposed,
1691                    outcomes: outcomes_matrix,
1692                    intermediary1,
1693                    intermediary2,
1694                    result,
1695                    ..
1696                } = &mut *runtime;
1697
1698                {
1699                    let (rows, columns) = design.shape();
1700                    for column in 0..columns {
1701                        for row in 0..rows {
1702                            let v = unsafe { design.get_unchecked_mut((row, column)) };
1703                            *v = if column == 0 {
1704                                1.0
1705                            } else if column == 1 {
1706                                predictor_iter.next().unwrap()
1707                            } else {
1708                                if row == 0 {
1709                                    predictor_iter = predictor_original.clone();
1710                                }
1711                                predictor_iter.next().unwrap().powi(column as _)
1712                            };
1713                        }
1714                    }
1715                }
1716                design.transpose_to(transposed);
1717
1718                {
1719                    let rows = outcomes_matrix.nrows();
1720                    for (row, outcome) in (0..rows).zip(outcomes) {
1721                        let v = unsafe { outcomes_matrix.get_unchecked_mut((row, 0)) };
1722                        *v = outcome;
1723                    }
1724                }
1725
1726                transposed.mul_to(design, intermediary1);
1727
1728                if !intermediary1.try_inverse_mut() {
1729                    let im = std::mem::replace(intermediary1, DMatrix::zeros(0, 0));
1730                    let pseudo_inverse = im.pseudo_inverse(1e-8).unwrap();
1731                    *intermediary1 = pseudo_inverse;
1732                }
1733                *intermediary2 = &*intermediary1 * &*transposed;
1734                intermediary2.mul_to(outcomes_matrix, result);
1735
1736                PolynomialCoefficients {
1737                    coefficients: runtime.result.iter().copied().collect(),
1738                }
1739            })
1740        }
1741        #[cfg(feature = "arbitrary-precision")]
1742        fn polynomial_arbitrary(
1743            predictors: impl Iterator<Item = f64> + Clone,
1744            outcomes: impl Iterator<Item = f64>,
1745            len: usize,
1746            degree: usize,
1747        ) -> PolynomialCoefficients {
1748            use rug::ops::PowAssign;
1749            let precision = (64 + degree * 2) as u32;
1750            let old = arbitrary_linear_algebra::default_precision();
1751            arbitrary_linear_algebra::set_default_precision(precision);
1752
1753            let predictors = predictors.map(|x| {
1754                arbitrary_linear_algebra::FloatWrapper::from(rug::Float::with_val(precision, x))
1755            });
1756            let outcomes = outcomes.map(|y| {
1757                arbitrary_linear_algebra::FloatWrapper::from(rug::Float::with_val(precision, y))
1758            });
1759
1760            let predictor_original = predictors.clone();
1761            let mut predictor_iter = predictors;
1762
1763            let design =
1764                nalgebra::DMatrix::from_fn(len, degree + 1, |row: usize, column: usize| {
1765                    if column == 0 {
1766                        rug::Float::with_val(precision, 1.0_f64).into()
1767                    } else if column == 1 {
1768                        predictor_iter.next().unwrap()
1769                    } else {
1770                        if row == 0 {
1771                            predictor_iter = predictor_original.clone();
1772                        }
1773                        let mut f = predictor_iter.next().unwrap();
1774                        f.0.pow_assign(column as u32);
1775                        f
1776                    }
1777                });
1778
1779            let t = design.transpose();
1780            let outcomes = nalgebra::DMatrix::from_iterator(len, 1, outcomes);
1781            let result = ((&t * &design).try_inverse().unwrap() * &t) * outcomes;
1782
1783            arbitrary_linear_algebra::set_default_precision(old);
1784
1785            PolynomialCoefficients {
1786                coefficients: result.iter().map(|f| f.0.to_f64()).collect(),
1787            }
1788        }
1789
1790        debug_assert!(degree < len, "degree + 1 must be less than or equal to len");
1791
1792        #[cfg(feature = "arbitrary-precision")]
1793        if degree < 10 {
1794            polynomial_simple_preallocated(predictors, outcomes, len, degree)
1795        } else {
1796            polynomial_arbitrary(predictors, outcomes, len, degree)
1797        }
1798        #[cfg(not(feature = "arbitrary-precision"))]
1799        polynomial_simple_preallocated(predictors, outcomes, len, degree)
1800    }
1801}
1802
1803/// [Theil-Sen estimator](https://en.wikipedia.org/wiki/Theil%E2%80%93Sen_estimator), a robust
1804/// linear (also implemented as polynomial) estimator.
1805/// Up to ~27% of values can be *outliers* - erroneous data far from the otherwise good data -
1806/// without large effects on the result.
1807///
1808/// [`LinearTheilSen`] implements [`LinearEstimator`].
1809pub mod theil_sen {
1810    use super::*;
1811    use crate::{percentile, F64OrdHash};
1812    use std::fmt::Debug;
1813
1814    /// A buffer returned by [`PermutationIter`] to avoid allocations.
1815    pub struct PermutationIterBuffer<T> {
1816        buf: Vec<(T, T)>,
1817    }
1818    impl<T> Deref for PermutationIterBuffer<T> {
1819        type Target = [(T, T)];
1820        fn deref(&self) -> &Self::Target {
1821            &self.buf
1822        }
1823    }
1824    /// An iterator over the permutations.
1825    #[derive(Debug)]
1826    pub struct PermutationIter<'a, T> {
1827        s1: &'a [T],
1828        s2: &'a [T],
1829        iters: Vec<usize>,
1830        values: Option<Vec<(T, T)>>,
1831        values_backup: Vec<(T, T)>,
1832        pairs: usize,
1833    }
1834    impl<'a, T: Copy + Debug> PermutationIter<'a, T> {
1835        fn new(s1: &'a [T], s2: &'a [T], pairs: usize) -> Self {
1836            assert!(
1837                pairs > 1,
1838                "each coordinate pair must be associated with at least one."
1839            );
1840            assert_eq!(s1.len(), s2.len());
1841            assert!(pairs <= s1.len());
1842            let iters = Vec::with_capacity(pairs);
1843            let values_backup = Vec::with_capacity(pairs);
1844            let mut me = Self {
1845                s1,
1846                s2,
1847                iters,
1848                values: None,
1849                values_backup,
1850                pairs,
1851            };
1852            for i in 0..pairs {
1853                // `+ (not last) as usize` since the last iterator doesn't first read vec.
1854                me.iters.push(i + usize::from(i + 1 < pairs) - 1);
1855            }
1856            #[allow(clippy::needless_range_loop)] // clarity
1857            for i in 0..pairs - 1 {
1858                me.values_backup.push((me.s1[i], me.s2[i]));
1859            }
1860            me.values_backup.push(me.values_backup[0]);
1861            me.values = Some(me.values_backup.clone());
1862            me
1863        }
1864        /// Please hand the buffer back after each iteration. This greatly reduces allocations.
1865        #[inline(always)]
1866        pub fn give_buffer(&mut self, buf: PermutationIterBuffer<T>) {
1867            debug_assert!(self.values.is_none());
1868            self.values = Some(buf.buf)
1869        }
1870        /// Collects the permutations in `pairs` number of [`Vec`]s, with `returned[i]` containing
1871        /// the i-th pair.
1872        pub fn collect_by_index(mut self) -> Vec<Vec<(T, T)>> {
1873            let mut vecs = Vec::with_capacity(self.pairs);
1874            for _ in 0..self.pairs {
1875                vecs.push(Vec::new());
1876            }
1877            while let Some(buf) = self.next() {
1878                for (pos, pair) in buf.iter().enumerate() {
1879                    vecs[pos].push(*pair)
1880                }
1881
1882                self.give_buffer(buf);
1883            }
1884            vecs
1885        }
1886        /// Collects `LEN` pairs from this iterator in a [`Vec`].
1887        ///
1888        /// # Panics
1889        ///
1890        /// `LEN` must be the same length as `pairs` supplied to [`permutations_generic`].
1891        pub fn collect_len<const LEN: usize>(mut self) -> Vec<[(T, T); LEN]> {
1892            let mut vec = Vec::new();
1893            while let Some(buf) = self.next() {
1894                let array = <[(T, T); LEN]>::try_from(&*buf).expect(
1895                    "tried to collect with set len, but permutations returned different len",
1896                );
1897                vec.push(array);
1898                self.give_buffer(buf);
1899            }
1900            vec
1901        }
1902    }
1903    impl<'a, T: Copy + Debug> Iterator for PermutationIter<'a, T> {
1904        type Item = PermutationIterBuffer<T>;
1905        #[inline]
1906        fn next(&mut self) -> Option<Self::Item> {
1907            for (num, iter) in self.iters.iter_mut().enumerate().rev() {
1908                *iter += 1;
1909
1910                if let Some(value) = self.s1.get(*iter) {
1911                    // SAFETY: they are the same length, so getting from one guarantees we can get
1912                    // the same index from the other one.
1913                    let next = (*value, *unsafe { self.s2.get_unchecked(*iter) });
1914
1915                    let values = &mut self.values_backup;
1916                    if let Some(v) = self.values.as_mut() {
1917                        // SAFETY: The length of `self.values`, `self.values_backup`, and
1918                        // `self.iters` are equal. Therefore, we can get the index of `self.values`
1919                        // returned by the enumeration of `self.iters`
1920                        *unsafe { v.get_unchecked_mut(num) } = next;
1921                    }
1922                    // SAFETY: See above.
1923                    *unsafe { values.get_unchecked_mut(num) } = next;
1924                    if num + 1 == self.pairs {
1925                        let values = match self.values.take() {
1926                            Some(x) => x,
1927                            None => self.values_backup.clone(),
1928                        };
1929                        return Some(PermutationIterBuffer { buf: values });
1930                    } else {
1931                        // optimization - if items left is less than what is required to fill the "tower"
1932                        // of succeeding indices, we return
1933                        if self.s1.len() - *iter <= self.pairs - 1 - num {
1934                            continue;
1935                        }
1936
1937                        // Not pushing unsafe/inline as hard here, as this isn't as hot of a path.
1938
1939                        #[allow(clippy::needless_range_loop)] // clarity
1940                        for i in num + 1..self.pairs {
1941                            // start is 1+ the previous?
1942                            let new = self.iters[i - 1] + usize::from(i + 1 < self.pairs);
1943                            self.iters[i] = new;
1944                            // fix values for lower iterators than the top one
1945                            if i + 1 < self.pairs {
1946                                if new >= self.s1.len() {
1947                                    continue;
1948                                }
1949                                values[i] = (self.s1[new], self.s2[new]);
1950                                if let Some(v) = self.values.as_mut() {
1951                                    v[i] = (self.s1[new], self.s2[new]);
1952                                }
1953                            }
1954                        }
1955                        return self.next();
1956                    }
1957                }
1958            }
1959            None
1960        }
1961    }
1962    /// The returned iterator is a bit funky.
1963    /// It returns a buffer, which at all costs should be reused.
1964    /// This could either be done using a while loop
1965    /// (e.g. `while let Some(buf) = iter.next() { iter.give_buffer(buf) }`)
1966    /// or any of the [built-in methods](PermutationIter).
1967    /// If you know the length at compile time, use [`PermutationIter::collect_len`].
1968    pub fn permutations_generic<'a, T: Copy + Debug>(
1969        s1: &'a [T],
1970        s2: &'a [T],
1971        pairs: usize,
1972    ) -> PermutationIter<'a, T> {
1973        PermutationIter::new(s1, s2, pairs)
1974    }
1975    /// Lower-bound estimate (up to `pairs > 20`), within 100x (which is quite good for factorials).
1976    ///
1977    /// The original equation is `elements!/(pairs! (elements - pairs)!)`
1978    #[inline]
1979    pub fn estimate_permutation_count(elements: usize, pairs: usize) -> f64 {
1980        let e = elements as f64;
1981        let p = pairs as f64;
1982        e.powf(p) / (p.powf(p - 0.8))
1983    }
1984    /// An exact count of permutations.
1985    /// Returns [`None`] if the arithmetic can't fit.
1986    #[inline]
1987    pub fn permutation_count(elements: usize, pairs: usize) -> Option<usize> {
1988        fn factorial(num: u128) -> Option<u128> {
1989            match num {
1990                0 | 1 => Some(1),
1991                _ => factorial(num - 1)?.checked_mul(num),
1992            }
1993        }
1994
1995        Some(
1996            (factorial(elements as _)?
1997                / (factorial(pairs as _)?.checked_mul(factorial((elements - pairs) as _)?))?)
1998                as usize,
1999        )
2000    }
2001
2002    /// Unique permutations of two elements - an iterator of all the pairs of associated values in the slices.
2003    ///
2004    /// This function will behave unexpectedly if `s1` and `s2` have different lengths.
2005    ///
2006    /// Returns an iterator which yields `O(n²)` items.
2007    // `TODO`: Make these return indices.
2008    pub fn permutations<'a, T: Copy>(
2009        s1: &'a [T],
2010        s2: &'a [T],
2011    ) -> impl Iterator<Item = ((T, T), (T, T))> + 'a {
2012        s1.iter()
2013            .zip(s2.iter())
2014            .enumerate()
2015            .flat_map(|(pos, (t11, t21))| {
2016                // +1 because we don't want our selves.
2017                let left = &s1[pos + 1..];
2018                let left_other = &s2[pos + 1..];
2019                left.iter()
2020                    .zip(left_other.iter())
2021                    .map(|(t12, t22)| ((*t11, *t21), (*t12, *t22)))
2022            })
2023    }
2024
2025    /// Linear estimation using the Theil-Sen estimatior. This is robust against outliers.
2026    /// `O(n²)`
2027    pub struct LinearTheilSen;
2028    impl LinearEstimator for LinearTheilSen {
2029        #[inline]
2030        fn model_linear(&self, predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
2031            slow_linear(predictors, outcomes)
2032        }
2033    }
2034    /// Polynomial estimation using the Theil-Sen estimatior. Very slow and should probably not be
2035    /// used.
2036    /// `O(n^degree)`
2037    pub struct PolynomialTheilSen;
2038    impl PolynomialEstimator for PolynomialTheilSen {
2039        #[inline]
2040        fn model_polynomial(
2041            &self,
2042            predictors: &[f64],
2043            outcomes: &[f64],
2044            degree: usize,
2045        ) -> PolynomialCoefficients {
2046            slow_polynomial(predictors, outcomes, degree)
2047        }
2048    }
2049
2050    /// Naive Theil-Sen implementation, which checks each line.
2051    ///
2052    /// Time & space: O(n²)
2053    ///
2054    /// # Panics
2055    ///
2056    /// Panics if `predictors.len() != outcomes.len()`.
2057    pub fn slow_linear(predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
2058        assert_eq!(predictors.len(), outcomes.len());
2059        // I've isolated the `Vec`s into blocks so we only have one at a time.
2060        // This reduces memory usage.
2061        let median_slope = {
2062            let slopes = permutations(predictors, outcomes).map(|((x1, y1), (x2, y2))| {
2063                // Δy/Δx
2064                (y1 - y2) / (x1 - x2)
2065            });
2066            let mut slopes: Vec<_> = slopes.map(F64OrdHash).collect();
2067
2068            percentile::median(&mut slopes).resolve()
2069        };
2070
2071        //// Old intersect code. Gets the median of all x components, and the median of all y
2072        //// components. We then use that as a point to extrapolate the intersection.
2073        //
2074        // let predictor_median = {
2075        // let mut predictors = predictors.to_vec();
2076        // let predictors = F64OrdHash::from_mut_f64_slice(&mut predictors);
2077        // percentile::median(predictors).resolve()
2078        // };
2079        // let outcome_median = {
2080        // let mut outcomes = outcomes.to_vec();
2081        // let outcomes = F64OrdHash::from_mut_f64_slice(&mut outcomes);
2082        // percentile::median(outcomes).resolve()
2083        // };
2084        // y=slope * x + intersect
2085        // y - slope * x = intersect
2086        // let intersect = outcome_median - median_slope * predictor_median;
2087
2088        // New intersect. This works by getting the median point by it's y value. Then, we
2089        // extrapolate from that.
2090        //
2091        // This produces much better results, but isn't what's commonly used.
2092        //
2093        // See https://stats.stackexchange.com/a/96166
2094        // for reference.
2095        let median = {
2096            #[derive(Debug, Clone, Copy)]
2097            struct CmpFirst<T, V>(T, V);
2098            impl<T: PartialEq, V> PartialEq for CmpFirst<T, V> {
2099                #[inline]
2100                fn eq(&self, other: &Self) -> bool {
2101                    self.0.eq(&other.0)
2102                }
2103            }
2104            impl<T: PartialEq + Eq, V> Eq for CmpFirst<T, V> {}
2105            impl<T: PartialOrd, V> PartialOrd for CmpFirst<T, V> {
2106                #[inline]
2107                fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2108                    self.0.partial_cmp(&other.0)
2109                }
2110            }
2111            impl<T: Ord, V> Ord for CmpFirst<T, V> {
2112                #[inline]
2113                fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2114                    self.0.cmp(&other.0)
2115                }
2116            }
2117
2118            let mut values: Vec<_> = predictors.iter().zip(outcomes.iter()).collect();
2119            match percentile::percentile_default_pivot_by(
2120                &mut values,
2121                crate::Fraction::HALF,
2122                &mut |a, b| F64OrdHash::f64_cmp(*a.1, *b.1),
2123            ) {
2124                percentile::MeanValue::Single(v) => (*v.0, *v.1),
2125                percentile::MeanValue::Mean(v1, v2) => ((v1.0 + v2.0) / 2.0, (v1.1 + v2.1) / 2.0),
2126            }
2127        };
2128        let intersect = median.1 - median.0 * median_slope;
2129
2130        LinearCoefficients {
2131            k: median_slope,
2132            m: intersect,
2133        }
2134    }
2135
2136    /// Naive Theil-Sen implementation, which checks each polynomial.
2137    ///
2138    /// Time & space: O(n^m) where m is `degree + 1`.
2139    ///
2140    /// # Panics
2141    ///
2142    /// Panics if `predictors.len() != outcomes.len()`.
2143    pub fn slow_polynomial(
2144        predictors: &[f64],
2145        outcomes: &[f64],
2146        degree: usize,
2147    ) -> PolynomialCoefficients {
2148        assert_eq!(predictors.len(), outcomes.len());
2149
2150        // if degree == 0, get median.
2151        if degree == 0 {
2152            let mut outcomes = outcomes.to_vec();
2153            let constant = crate::percentile::percentile_default_pivot_by(
2154                &mut outcomes,
2155                crate::Fraction::HALF,
2156                &mut |a, b| crate::F64OrdHash::f64_cmp(*a, *b),
2157            )
2158            .resolve();
2159            return PolynomialCoefficients {
2160                coefficients: vec![constant],
2161            };
2162        }
2163
2164        // init
2165        let mut iter = permutations_generic(predictors, outcomes, degree + 1);
2166        let mut coefficients = Vec::with_capacity(degree + 1);
2167        let permutations_count = permutation_count(predictors.len(), degree + 1)
2168            .unwrap_or_else(|| estimate_permutation_count(predictors.len(), degree + 1) as usize);
2169        for _ in 0..degree + 1 {
2170            // now that's a lot of allocations.
2171            coefficients.push(Vec::with_capacity(permutations_count))
2172        }
2173
2174        // Hard-code some of these to increase performance. Else, we'd have to do linear algebra to
2175        // get the equation of a straight line from two points.
2176        match degree {
2177            0 => unreachable!("we handled this above"),
2178            1 => {
2179                while let Some(buf) = iter.next() {
2180                    debug_assert_eq!(buf.len(), 2);
2181
2182                    // SAFETY: I know the buf is going to be exactly 2 in length, as I wrote the
2183                    // code.
2184                    let p1 = unsafe { buf.get_unchecked(0) };
2185                    let x1 = p1.0;
2186                    let y1 = p1.1;
2187                    let p2 = unsafe { buf.get_unchecked(1) };
2188                    let x2 = p2.0;
2189                    let y2 = p2.1;
2190
2191                    let slope = (y1 - y2) / (x1 - x2);
2192                    // y=slope * x + intersect
2193                    // intersect = y - slope * x
2194                    // we could've chosen p2, it doesn't matter.
2195                    let intersect = y1 - x1 * slope;
2196
2197                    // SAFETY: we pushed these vecs to `coefficients` above.
2198                    unsafe {
2199                        coefficients.get_unchecked_mut(1).push(slope);
2200                        coefficients.get_unchecked_mut(0).push(intersect);
2201                    }
2202
2203                    iter.give_buffer(buf);
2204                }
2205            }
2206            // 10x performance increase with this hand-crafted technique.
2207            2 => {
2208                while let Some(buf) = iter.next() {
2209                    debug_assert_eq!(buf.len(), 3);
2210
2211                    // SAFETY: I know the buf is going to be exactly 2 in length, as I wrote the
2212                    // code.
2213                    let p1 = unsafe { buf.get_unchecked(0) };
2214                    let x1 = p1.0;
2215                    let y1 = p1.1;
2216                    let p2 = unsafe { buf.get_unchecked(1) };
2217                    let x2 = p2.0;
2218                    let y2 = p2.1;
2219                    let p3 = unsafe { buf.get_unchecked(2) };
2220                    let x3 = p3.0;
2221                    let y3 = p3.1;
2222
2223                    // Derived from the systems of equation this makes.
2224                    // See https://math.stackexchange.com/a/680695
2225
2226                    let a = (x1 * (y3 - y2) + x2 * (y1 - y3) + x3 * (y2 - y1))
2227                        / ((x1 - x2) * (x1 - x3) * (x2 - x3));
2228                    let b = (y2 - y1) / (x2 - x1) - a * (x1 + x2);
2229                    let c = y1 - a * x1 * x1 - b * x1;
2230
2231                    // SAFETY: we pushed these vecs to `coefficients` above.
2232                    unsafe {
2233                        coefficients.get_unchecked_mut(2).push(a);
2234                        coefficients.get_unchecked_mut(1).push(b);
2235                        coefficients.get_unchecked_mut(0).push(c);
2236                    }
2237
2238                    iter.give_buffer(buf);
2239                }
2240            }
2241            #[cfg(not(feature = "ols"))]
2242            _ => {
2243                panic!("unsupported degree for polynomial Theil-Sen. Supports 1,2 without the OLS cargo feature.");
2244            }
2245            #[cfg(feature = "ols")]
2246            _ => {
2247                while let Some(buf) = iter.next() {
2248                    #[inline(always)]
2249                    fn tuple_first(t: &(f64, f64)) -> f64 {
2250                        t.0
2251                    }
2252                    #[inline(always)]
2253                    fn tuple_second(t: &(f64, f64)) -> f64 {
2254                        t.1
2255                    }
2256
2257                    debug_assert_eq!(buf.len(), degree + 1);
2258
2259                    let predictors = buf.iter().map(tuple_first);
2260                    let outcomes = buf.iter().map(tuple_second);
2261
2262                    let polynomial = ols::polynomial(predictors, outcomes, degree + 1, degree);
2263                    for (pos, coefficient) in polynomial.iter().enumerate() {
2264                        // SAFETY: we pushed these vecs to `coefficients` above.
2265                        // pos is less than the size of `coefficients`.
2266                        unsafe { coefficients.get_unchecked_mut(pos).push(*coefficient) };
2267                    }
2268
2269                    iter.give_buffer(buf);
2270                }
2271            }
2272        }
2273
2274        #[inline(always)]
2275        fn f64_cmp(a: &f64, b: &f64) -> std::cmp::Ordering {
2276            crate::F64OrdHash::f64_cmp(*a, *b)
2277        }
2278
2279        let mut result = Vec::with_capacity(degree + 1);
2280        for mut coefficients in coefficients {
2281            // `TODO`: Choose coefficients for a single point (the median of the coefficient with the
2282            // highest exponential) instead of then median of the single values.
2283
2284            // 5x boost in performance here when using `O(n)` median instead of sorting. (when
2285            // using args `-t -d5` with a detaset of 40 values).
2286            let median = crate::percentile::percentile_default_pivot_by(
2287                &mut coefficients,
2288                crate::Fraction::HALF,
2289                &mut f64_cmp,
2290            )
2291            .resolve();
2292            result.push(median);
2293        }
2294        PolynomialCoefficients {
2295            coefficients: result,
2296        }
2297    }
2298
2299    #[cfg(test)]
2300    mod tests {
2301        use super::*;
2302
2303        #[test]
2304        fn permutations_eq_1() {
2305            let s1 = [1., 2., 3., 4., 5.];
2306            let s2 = [2., 4., 6., 8., 10.];
2307
2308            let permutations1 = permutations(&s1, &s2)
2309                .map(|(x, y)| [x, y])
2310                .collect::<Vec<_>>();
2311            let permutations2 = permutations_generic(&s1, &s2, 2).collect_len();
2312
2313            assert_eq!(permutations1, permutations2);
2314        }
2315        #[test]
2316        #[cfg(feature = "rand")]
2317        fn permutations_eq_2() {
2318            use rand::Rng;
2319
2320            let mut s1 = [0.0; 20];
2321            let mut s2 = [0.0; 20];
2322
2323            let mut rng = rand::thread_rng();
2324            rng.fill(&mut s1);
2325            rng.fill(&mut s2);
2326
2327            let permutations1 = permutations(&s1, &s2)
2328                .map(|(x, y)| [x, y])
2329                .collect::<Vec<_>>();
2330            let permutations2 = permutations_generic(&s1, &s2, 2).collect_len();
2331
2332            assert_eq!(permutations1, permutations2);
2333        }
2334        #[test]
2335        fn permutations_len_3() {
2336            let s1 = [1., 2., 3., 4., 5.];
2337            let s2 = [2., 4., 6., 8., 10.];
2338
2339            let permutations = permutations_generic(&s1, &s2, 3).collect_len::<3>();
2340
2341            let expected: &[[(f64, f64); 3]] = &[
2342                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0)],
2343                [(1.0, 2.0), (2.0, 4.0), (4.0, 8.0)],
2344                [(1.0, 2.0), (2.0, 4.0), (5.0, 10.0)],
2345                [(1.0, 2.0), (3.0, 6.0), (4.0, 8.0)],
2346                [(1.0, 2.0), (3.0, 6.0), (5.0, 10.0)],
2347                [(1.0, 2.0), (4.0, 8.0), (5.0, 10.0)],
2348                [(2.0, 4.0), (3.0, 6.0), (4.0, 8.0)],
2349                [(2.0, 4.0), (3.0, 6.0), (5.0, 10.0)],
2350                [(2.0, 4.0), (4.0, 8.0), (5.0, 10.0)],
2351                [(3.0, 6.0), (4.0, 8.0), (5.0, 10.0)],
2352            ];
2353
2354            assert_eq!(expected.len(), permutation_count(5, 3).unwrap());
2355
2356            assert_eq!(permutations, expected,);
2357        }
2358        #[test]
2359        fn permutations_len_4_1() {
2360            let s1 = [1., 2., 3., 4., 5.];
2361            let s2 = [2., 4., 6., 8., 10.];
2362
2363            let permutations = permutations_generic(&s1, &s2, 4).collect_len();
2364
2365            let expected: &[[(f64, f64); 4]] = &[
2366                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0), (4.0, 8.0)],
2367                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0), (5.0, 10.0)],
2368                [(1.0, 2.0), (2.0, 4.0), (4.0, 8.0), (5.0, 10.0)],
2369                [(1.0, 2.0), (3.0, 6.0), (4.0, 8.0), (5.0, 10.0)],
2370                [(2.0, 4.0), (3.0, 6.0), (4.0, 8.0), (5.0, 10.0)],
2371            ];
2372
2373            assert_eq!(expected.len(), permutation_count(5, 4).unwrap());
2374
2375            assert_eq!(permutations, expected,);
2376        }
2377        #[test]
2378        fn permutations_len_4_2() {
2379            let s1 = [1., 2., 3., 4., 5., 6.];
2380            let s2 = [2., 4., 6., 8., 10., 12.];
2381
2382            let permutations = permutations_generic(&s1, &s2, 4).collect_len();
2383
2384            let expected: &[[(f64, f64); 4]] = &[
2385                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0), (4.0, 8.0)],
2386                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0), (5.0, 10.0)],
2387                [(1.0, 2.0), (2.0, 4.0), (3.0, 6.0), (6.0, 12.0)],
2388                [(1.0, 2.0), (2.0, 4.0), (4.0, 8.0), (5.0, 10.0)],
2389                [(1.0, 2.0), (2.0, 4.0), (4.0, 8.0), (6.0, 12.0)],
2390                [(1.0, 2.0), (2.0, 4.0), (5.0, 10.0), (6.0, 12.0)],
2391                [(1.0, 2.0), (3.0, 6.0), (4.0, 8.0), (5.0, 10.0)],
2392                [(1.0, 2.0), (3.0, 6.0), (4.0, 8.0), (6.0, 12.0)],
2393                [(1.0, 2.0), (3.0, 6.0), (5.0, 10.0), (6.0, 12.0)],
2394                [(1.0, 2.0), (4.0, 8.0), (5.0, 10.0), (6.0, 12.0)],
2395                [(2.0, 4.0), (3.0, 6.0), (4.0, 8.0), (5.0, 10.0)],
2396                [(2.0, 4.0), (3.0, 6.0), (4.0, 8.0), (6.0, 12.0)],
2397                [(2.0, 4.0), (3.0, 6.0), (5.0, 10.0), (6.0, 12.0)],
2398                [(2.0, 4.0), (4.0, 8.0), (5.0, 10.0), (6.0, 12.0)],
2399                [(3.0, 6.0), (4.0, 8.0), (5.0, 10.0), (6.0, 12.0)],
2400            ];
2401
2402            assert_eq!(expected.len(), permutation_count(6, 4).unwrap());
2403
2404            assert_eq!(permutations, expected,);
2405        }
2406    }
2407}
2408
2409/// Spiral estimator, a robust sampling estimator.
2410/// This should be more robust than [`theil_sen`].
2411///
2412/// > This is a brainchild of this library's lead developer [Icelk](mailto:Icelk<main@icelk.dev>).
2413///
2414/// The [`spiral::Options`] implement most of the [estimators](models).
2415///
2416/// # Advantages
2417///
2418/// You supply a `fitness_function` to all functions which tells the algorithm which lines are
2419/// good. The magnitude is irrelevant, only order is considered. The algorithm tries to *minimize*
2420/// the returned value. **This allows you to choose the desired properties of resulting
2421/// line/polynomial, without checking all possible values.**
2422///
2423/// # Caveats
2424///
2425/// The polynomial regression implementation only allows for degrees 1 & 2.
2426/// See [details](#details) for more info on this.
2427///
2428/// The sampling technique means this might miss the right point to close in on. Therefore, I
2429/// highly recommend using the [higher quality options](spiral::Options::new).
2430///
2431/// ## Robustness
2432///
2433/// Since this uses a fitness function, the robustness is determined by that. Using the "default"
2434/// `manhattan_distance` gives good results (think least squares, but without the squared
2435/// importance of errors). This is what the implementations for [`spiral::Options`] does.
2436///
2437/// Since this tests a wide range of possibilities before deciding on one, it's very likely we
2438/// don't get trapped in a local maxima.
2439///
2440/// # Performance
2441///
2442/// The functions are `O(fitness function)` where `O(fitness function)` is the time
2443/// complexity of your `fitness_function`. That's often `O(n)` as you'd probably in some way
2444/// sum up the points relative to the model.
2445///
2446/// This puts the algorithm similar to [`ols`], but with much worse (read: 4x-100x) performance.
2447/// This may be justified by the [advantages](#advantages).
2448/// It scales much better than [`theil_sen`] and is more robust, but when the count of points is
2449/// small, `theil_sen` is faster.
2450///
2451/// # Details
2452///
2453/// The idea is to make a [phase space](https://en.wikipedia.org/wiki/Phase_space)
2454/// of the parameters to a line (`y=(slope)*x + (y-intersect)`). We then traverse the phase space
2455/// with a [logarithmic spiral](https://en.wikipedia.org/wiki/Logarithmic_spiral)
2456/// and sample points (we start at the angle θ e.g. `-12π` and go to a max value, e.g. `12π`)
2457/// on an interval. When the range of the spiral has been sampled, we choose the best point and
2458/// create a spiral there. Depending on how far out the new point, scale the whole spiral's size
2459/// (by `distance.sqrt().sqrt()`). Then repeat.
2460///
2461/// Parameters are chosen for an optimal spiral. The logarithmic spiral was chosen due to the
2462/// distribution of unknown numbers (which the coefficients of the line are). There's generally
2463/// more numbers in the range 0..100 than in 100..200. Therefore, we search more numbers in 0..100.
2464///
2465/// We can do this in 3 dimensions by creating a 3d spiral. For this, I used a
2466/// [spherical spiral](https://en.wikipedia.org/wiki/Spiral#Spherical_spirals)
2467/// where the radius grows with the angle of the spiral, calculated by `e^(θk)` where `k` is a
2468/// parameter, similar to how a logarithmic spiral is created.
2469///
2470/// > On implementing third degree polynomials,
2471/// > can we get a spiral on a hypersphere? Or do we just need a even distribution?
2472///
2473/// See [`spiral::Options`] for more info on the parameters.
2474pub mod spiral {
2475    use super::*;
2476    use std::f64::consts::{E, TAU};
2477    use std::ops::Range;
2478    use utils::*;
2479
2480    /// Samples points on a logarithmic spiral in the phase space of all possible straight lines.
2481    ///
2482    /// See [`Options`].
2483    ///
2484    /// Can be used for models other than linear, as this just optimizes two floats according to
2485    /// `fitness_function`. The returned values are the best match.
2486    pub fn two_variable_optimization(
2487        fitness_function: impl Fn([f64; 2]) -> f64,
2488        options: Options,
2489    ) -> [f64; 2] {
2490        let Options {
2491            exponent_coefficient,
2492            angle_coefficient,
2493            num_lockon,
2494            samples_per_rotation,
2495            range,
2496            turns: _,
2497        } = options;
2498        let advance = TAU / samples_per_rotation;
2499        let mut best = ((f64::MIN, [1.; 2], 1.), [0.; 2]);
2500        let mut last_best = f64::MIN;
2501
2502        let mut exponent_coefficients = [exponent_coefficient; 2];
2503
2504        for i in 0..num_lockon {
2505            let mut theta = range.start;
2506            while theta < range.end {
2507                let r = E.powf(theta * angle_coefficient);
2508                let a0 = r * theta.cos();
2509                let b0 = r * theta.sin();
2510                let a = a0 * exponent_coefficients[0] + best.1[0];
2511                let b = b0 * exponent_coefficients[1] + best.1[1];
2512
2513                let coeffs = [a, b];
2514
2515                let fitness = fitness_function(coeffs);
2516                if fitness > best.0 .0 {
2517                    best = ((fitness, [a0, b0], r), coeffs);
2518                }
2519
2520                theta += advance;
2521            }
2522            // If the best didn't change, we aren't going to find better results.
2523            if last_best == best.0 .0 && i != 0 {
2524                return best.1;
2525            }
2526            // Update "zoom" of spiral
2527            // don't go full out, "ease" this into several approaching steps with .sqrt to avoid
2528            // overcorrection.
2529            let best_size = best.0;
2530            exponent_coefficients[0] *= (best_size.1[0].abs() + best_size.2 / 32.).sqrt();
2531            exponent_coefficients[1] *= (best_size.1[1].abs() + best_size.2 / 32.).sqrt();
2532
2533            last_best = best.0 .0;
2534
2535            // uncomment line below to see how the coefficient changes and the current best.
2536            // reveals how it shrinks the spiral, and sometimes enlarges it to later zoom in (if
2537            // enough iterations are allowed)
2538            //
2539            // println!("Iteration complete. exponent_coefficients: {exponent_coefficients:.3?} best: {best:.3?}");
2540        }
2541        best.1
2542    }
2543    /// Samples points on a spherical spiral in the phase space of all second degree polynomials.
2544    /// As θ (the angle) increases, the imaginary sphere's size is increased.
2545    /// This gives a good distribution of sample points in 3d space.
2546    ///
2547    /// See [`Options`].
2548    ///
2549    /// This function just optimizes three floats according to `fitness_function`.
2550    /// The returned value is the best match.
2551    pub fn three_variable_optimization(
2552        fitness_function: impl Fn([f64; 3]) -> f64,
2553        options: Options,
2554    ) -> [f64; 3] {
2555        // See the function above for more documentation.
2556        // This is the same, but with three dimensions instead.
2557        let Options {
2558            exponent_coefficient,
2559            angle_coefficient,
2560            num_lockon,
2561            samples_per_rotation,
2562            range,
2563            turns,
2564        } = options;
2565        let advance = TAU / samples_per_rotation;
2566
2567        let mut best = ((f64::MIN, [1.; 3], 1.), [0.; 3]);
2568        let mut last_best = f64::MIN;
2569
2570        let mut exponent_coefficients = [exponent_coefficient; 3];
2571
2572        for i in 0..num_lockon {
2573            let mut theta = range.start;
2574            while theta < range.end {
2575                let r = E.powf(theta * angle_coefficient);
2576                let a0 = r * theta.sin() * (turns * theta).cos();
2577                let b0 = r * theta.sin() * (turns * theta).sin();
2578                let c0 = r * theta.cos();
2579                let a = a0 * exponent_coefficients[0] + best.1[0];
2580                let b = b0 * exponent_coefficients[1] + best.1[1];
2581                let c = c0 * exponent_coefficients[2] + best.1[2];
2582
2583                let coeffs = [a, b, c];
2584
2585                let fitness = fitness_function(coeffs);
2586                if fitness > best.0 .0 {
2587                    best = ((fitness, [a0, b0, c0], r), coeffs);
2588                }
2589
2590                theta += advance;
2591            }
2592            if last_best == best.0 .0 && i != 0 {
2593                return best.1;
2594            }
2595
2596            let best_size = best.0;
2597            exponent_coefficients[0] *= (best_size.1[0].abs() + best_size.2 / 32.).sqrt();
2598            exponent_coefficients[1] *= (best_size.1[1].abs() + best_size.2 / 32.).sqrt();
2599            exponent_coefficients[2] *= (best_size.1[2].abs() + best_size.2 / 32.).sqrt();
2600
2601            last_best = best.0 .0;
2602        }
2603        best.1
2604    }
2605
2606    /// Options for the spiral.
2607    ///
2608    /// This also implements most [estimator](models) traits.
2609    /// These all use the manhattan distance as their fitness function.
2610    /// The estimators have `O(n)` runtime performance and `O(1)` size performance.
2611    ///
2612    /// > Polynomial estimator only supports degrees 1 & 2.
2613    ///
2614    /// See [module-level documentation](self) for more info about concepts.
2615    ///
2616    /// > The [`Self::range`] limit samples on the spiral. This causes a upper limit for
2617    /// > coefficients and a lower "precision" cutoff. You can increase
2618    /// > [`Self::exponent_coefficient`] if you know your data will have large numbers.
2619    /// > The algorithm will increase the size of the spiral of a line outside the spiral is found.
2620    ///
2621    /// Use a graphing tool (e.g. Desmos) and plot `r=ae^(kθ)`.
2622    /// a is [`Self::exponent_coefficient`].
2623    /// k is [`Self::angle_coefficient`].
2624    ///
2625    /// To keep the same "size" of the spiral, you have to multiply both ends of [`Self::range`]
2626    /// with the factor of [`Self::angle_coefficient`].
2627    ///
2628    /// # Performance
2629    ///
2630    /// The methods are `O(1)*O(fitness_function)` where `O(fitness_function)` is the time
2631    /// complexity of your `fitness_function`. That's often `O(n)` as you'd probably in some way
2632    /// sum up the points relative to the model.
2633    ///
2634    /// The following options affect the performance as follows (roughly, no coefficients)
2635    /// `O(num_lockon * samples_per_rotation * range.length)`.
2636    ///
2637    /// Keep in mind you should probably not lower [`Self::angle_coefficient`] bellow `0.15`
2638    /// if you don't increase the [`Self::range`].
2639    #[must_use]
2640    #[derive(Debug, Clone, PartialEq)]
2641    pub struct Options {
2642        /// The initial scale of the spiral.
2643        ///
2644        /// This gets adjusted when locking on.
2645        pub exponent_coefficient: f64,
2646        /// The "density" of the spiral.
2647        pub angle_coefficient: f64,
2648        /// How many lockons we are allowed to do. This is a max value.
2649        pub num_lockon: usize,
2650        /// The count of samples per each rotation in the spiral.
2651        pub samples_per_rotation: f64,
2652        /// The range of angles to test.
2653        pub range: Range<f64>,
2654        /// The turns of the 3d spiral when using [`three_variable_optimization`].
2655        /// Frequency of turns per sphere. Is unnecessary to turn up when
2656        /// [`Self::samples_per_rotation`] is low.
2657        pub turns: f64,
2658    }
2659    impl Options {
2660        /// Create a new set of options with good defaults.
2661        ///
2662        /// `level` is allowed to be in the range [1..=9].
2663        /// Higher values are more "precise" - they take longer but are also way
2664        /// (especially `level>4`) more likely to return good results.
2665        ///
2666        /// Expect a 2-4x increase in runtime per increment of `level`.
2667        ///
2668        /// # Panics
2669        ///
2670        /// Panics if `!(1..=9).contains(level)`.
2671        pub fn new(level: u8) -> Self {
2672            if !(1..=9).contains(&level) {
2673                panic!("level of spiral::Options is out of bounds. Accepts 1..=9");
2674            }
2675            let level = level as usize - 1;
2676            // have settings for all levels (1 through 9)
2677            //
2678            // These values are based on my intuition of the algorithm.
2679            let num_lockon = [8, 8, 10, 12, 16, 16, 24, 32, 64];
2680            let angle_coefficient = [0.23, 0.23, 0.15, 0.13, 0.11, 0.09, 0.07, 0.05, 0.03];
2681            // these are odd values to avoid repeating the same angle on multiple turns
2682            let samples_per_rotation = [15., 19., 29., 34., 38., 49., 71., 115., 217.];
2683            let turns = [10., 12., 12., 14., 16., 16., 16., 16., 24.];
2684            let range = [
2685                -2.0..2.,
2686                -2.0..4.,
2687                -4.0..4.,
2688                -4.0..6.,
2689                -6.0..6.,
2690                -6.0..6.,
2691                -6.0..6.,
2692                -6.0..8.,
2693                -8.0..12.,
2694            ];
2695            let num_lockon = num_lockon[level];
2696            let angle_coefficient = angle_coefficient[level];
2697            let samples_per_rotation = samples_per_rotation[level];
2698            let turns = turns[level];
2699            let range = range[level].clone();
2700            let range = (range.start * TAU)..(range.end * TAU);
2701            Self {
2702                exponent_coefficient: 10.,
2703                angle_coefficient,
2704                num_lockon,
2705                samples_per_rotation,
2706                range,
2707                turns,
2708            }
2709        }
2710    }
2711    impl Default for Options {
2712        fn default() -> Self {
2713            Self::new(5)
2714        }
2715    }
2716
2717    pub(super) struct SecondDegreePolynomial(pub(super) [f64; 3]);
2718    impl Predictive for SecondDegreePolynomial {
2719        fn predict_outcome(&self, predictor: f64) -> f64 {
2720            self.0[0] + self.0[1] * predictor + self.0[2] * predictor * predictor
2721        }
2722    }
2723
2724    /// [`LinearEstimator`] for the spiral estimator using a fitness function and [`Options`]
2725    /// provided by you.
2726    /// `O(fitness function)`
2727    pub struct SpiralLinear<F: Fn(&LinearCoefficients, &[f64], &[f64]) -> f64>(pub F, pub Options);
2728    impl<F: Fn(&LinearCoefficients, &[f64], &[f64]) -> f64> LinearEstimator for SpiralLinear<F> {
2729        fn model_linear(&self, predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
2730            wrap_linear(two_variable_optimization(
2731                #[inline(always)]
2732                |model| self.0(&wrap_linear(model), predictors, outcomes),
2733                self.1.clone(),
2734            ))
2735        }
2736    }
2737
2738    macro_rules! impl_estimator {
2739        ($(
2740            $name:ident, $method:ident, $fn:ident, $ret:ident, $wrap:expr,
2741        )+) => {
2742            $(
2743                impl $name for Options {
2744                    fn $method(&self, predictors: &[f64], outcomes: &[f64]) -> $ret {
2745                        $wrap($fn(
2746                            #[inline(always)]
2747                            |model| manhattan_distance(&$wrap(model), predictors, outcomes),
2748                            self.clone(),
2749                        ))
2750                    }
2751                }
2752            )+
2753        };
2754    }
2755    macro_rules! impl_estimator_trig {
2756        ($(
2757            $name:ident, $method:ident, $fn:ident, $ret:ident, $wrap:expr,
2758        )+) => {
2759            $(
2760                impl $name for Options {
2761                    fn $method(&self, predictors: &[f64], outcomes: &[f64], max_frequency: f64) -> $ret {
2762                        $wrap($fn(
2763                            #[inline(always)]
2764                            |model| trig_adjusted_manhattan_distance(&$wrap(model), model, predictors, outcomes, max_frequency),
2765                            self.clone(),
2766                        ))
2767                    }
2768                }
2769            )+
2770        };
2771    }
2772    impl_estimator!(
2773        LinearEstimator,
2774        model_linear,
2775        two_variable_optimization,
2776        LinearCoefficients,
2777        wrap_linear,
2778        //
2779        PowerEstimator,
2780        model_power,
2781        two_variable_optimization,
2782        PowerCoefficients,
2783        wrap_power,
2784        //
2785        ExponentialEstimator,
2786        model_exponential,
2787        two_variable_optimization,
2788        ExponentialCoefficients,
2789        wrap_exponential,
2790        //
2791        LogisticEstimator,
2792        model_logistic,
2793        three_variable_optimization,
2794        LogisticCoefficients,
2795        wrap_logistic,
2796    );
2797    impl_estimator_trig!(
2798        SineEstimator,
2799        model_sine,
2800        three_variable_optimization,
2801        SineCoefficients,
2802        SineCoefficients::wrap,
2803        //
2804        CosineEstimator,
2805        model_cosine,
2806        three_variable_optimization,
2807        CosineCoefficients,
2808        CosineCoefficients::wrap,
2809        //
2810        TangentEstimator,
2811        model_tangent,
2812        three_variable_optimization,
2813        TangentCoefficients,
2814        TangentCoefficients::wrap,
2815        //
2816        SecantEstimator,
2817        model_secant,
2818        three_variable_optimization,
2819        SecantCoefficients,
2820        SecantCoefficients::wrap,
2821        //
2822        CosecantEstimator,
2823        model_cosecant,
2824        three_variable_optimization,
2825        CosecantCoefficients,
2826        CosecantCoefficients::wrap,
2827        //
2828        CotangentEstimator,
2829        model_cotangent,
2830        three_variable_optimization,
2831        CotangentCoefficients,
2832        CotangentCoefficients::wrap,
2833    );
2834    impl PolynomialEstimator for Options {
2835        fn model_polynomial(
2836            &self,
2837            predictors: &[f64],
2838            outcomes: &[f64],
2839            degree: usize,
2840        ) -> PolynomialCoefficients {
2841            match degree {
2842                1 => wrap_linear(two_variable_optimization(
2843                    #[inline(always)]
2844                    |model| manhattan_distance(&wrap_linear(model), predictors, outcomes),
2845                    self.clone(),
2846                ))
2847                .into(),
2848                2 => three_variable_optimization(
2849                    #[inline(always)]
2850                    |model| {
2851                        manhattan_distance(&SecondDegreePolynomial(model), predictors, outcomes)
2852                    },
2853                    self.clone(),
2854                )
2855                .into(),
2856                _ => panic!("unsupported degree for polynomial spiral. Supports 1,2."),
2857            }
2858        }
2859    }
2860
2861    /// Implements [`LogisticEstimator`] with a known ceiling for the input values.
2862    /// Uses manhattan distance as the fitness function.
2863    ///
2864    /// This can be used to model logistic growth with a known max.
2865    #[derive(Debug, Clone, PartialEq)]
2866    pub struct SpiralLogisticWithCeiling {
2867        /// The options of the spiral regression.
2868        pub opts: Options,
2869        /// The max value of the input values.
2870        /// This becomes [`LogisticCoefficients::l`].
2871        pub ceiling: f64,
2872    }
2873
2874    impl SpiralLogisticWithCeiling {
2875        /// Create a new estimator with `ceiling` and `opts`.
2876        pub fn new(opts: Options, ceiling: f64) -> Self {
2877            Self { opts, ceiling }
2878        }
2879    }
2880    impl LogisticEstimator for SpiralLogisticWithCeiling {
2881        fn model_logistic(&self, predictors: &[f64], outcomes: &[f64]) -> LogisticCoefficients {
2882            fn wrap(a: [f64; 2], max: f64) -> LogisticCoefficients {
2883                LogisticCoefficients {
2884                    x0: a[0],
2885                    l: max,
2886                    k: a[1],
2887                }
2888            }
2889            wrap(
2890                two_variable_optimization(
2891                    #[inline]
2892                    |model| manhattan_distance(&wrap(model, self.ceiling), predictors, outcomes),
2893                    self.opts.clone(),
2894                ),
2895                self.ceiling,
2896            )
2897        }
2898    }
2899}
2900
2901/// Assumes the fitness function has a minimal slope when the value is optimal (i.e. e.g.
2902/// `(x-4.).abs()` will not work, since it's slope is constant and then changes sign)
2903#[allow(missing_docs)]
2904pub mod gradient_descent {
2905    use super::*;
2906    use utils::BorrowedPolynomial;
2907
2908    pub struct ParallelOptions {
2909        pub learn_rate: f64,
2910        pub factor_decrease: f64,
2911        pub rough_max_sign_changes: usize,
2912        pub rough_slope_reduction_goal: f64,
2913        pub rough_iterations_base: usize,
2914        pub rough_iterations_per_degree: usize,
2915        pub fine_iterations_base: usize,
2916        pub fine_iterations_per_degree: usize,
2917    }
2918    impl Default for ParallelOptions {
2919        fn default() -> Self {
2920            Self {
2921                learn_rate: 50.,
2922                factor_decrease: 1.2,
2923                rough_max_sign_changes: 100,
2924                rough_slope_reduction_goal: 4.,
2925                rough_iterations_base: 64,
2926                rough_iterations_per_degree: 13,
2927                fine_iterations_base: 4,
2928                fine_iterations_per_degree: 3,
2929            }
2930        }
2931    }
2932    pub struct SimultaneousOptions {
2933        pub learn_rate: f64,
2934        pub factor_decrease: f64,
2935        pub factor_increase: f64,
2936        pub target_accuracy: f64,
2937    }
2938    impl SimultaneousOptions {
2939        pub fn new(target_accuracy: f64) -> Self {
2940            Self {
2941                learn_rate: 0.0001,
2942                factor_decrease: 1.5,
2943                factor_increase: 1.8,
2944                target_accuracy,
2945            }
2946        }
2947    }
2948    impl ParallelOptions {
2949        #[inline(always)]
2950        fn adjusted_slope(n: f64) -> f64 {
2951            let n = n / 8.;
2952            let ln = match n.partial_cmp(&0.) {
2953                Some(std::cmp::Ordering::Less) => -((-n + 1.).ln()),
2954                Some(std::cmp::Ordering::Greater) => (n + 1.).ln(),
2955                _ => 0.,
2956            };
2957            ln * 8.
2958        }
2959        pub fn polynomial_optimization(
2960            &self,
2961            n: usize,
2962            target_accuracy: f64,
2963            fitness_function: impl Fn(&[f64]) -> f64,
2964        ) -> Vec<f64> {
2965            let mut values: Vec<f64> = std::iter::repeat(0.).take(n).collect();
2966            let mut factors: Vec<f64> = std::iter::repeat(1.).take(n).collect();
2967            let dx = (target_accuracy / 2.).max(1e-11);
2968
2969            let get_slope = |dx: f64, i: usize, values: &mut [f64]| {
2970                let y1 = fitness_function(values);
2971                values[i] += dx;
2972                let y2 = fitness_function(values);
2973                values[i] -= dx;
2974                (y1 - y2) / dx
2975            };
2976
2977            let rough_iterations =
2978                self.rough_iterations_base + self.rough_iterations_per_degree * n;
2979            let fine_iterations = self.fine_iterations_base + self.fine_iterations_per_degree * n;
2980
2981            for _ in 0..rough_iterations {
2982                for i in 0..n {
2983                    let first_slope = get_slope(dx, i, &mut values);
2984                    let mut slope_positive = first_slope.is_sign_positive();
2985                    let mut factor = factors[i];
2986                    let mut sign_changes = 0;
2987                    loop {
2988                        let slope = get_slope(dx, i, &mut values);
2989                        if slope.is_sign_positive() != slope_positive {
2990                            slope_positive = !slope_positive;
2991                            factor /= self.factor_decrease;
2992                            sign_changes += 1;
2993                        }
2994                        values[i] += Self::adjusted_slope(slope) * self.learn_rate * factor;
2995                        // println!(
2996                        // "Slope {slope} add {} log {} factor {factor}",
2997                        // adjusted_slope(slope) * self.learn_rate * factor,
2998                        // adjusted_slope(slope),
2999                        // );
3000                        if slope.abs() < first_slope.abs() / self.rough_slope_reduction_goal
3001                            || sign_changes > self.rough_max_sign_changes
3002                            || slope.abs() < target_accuracy * 2.
3003                        {
3004                            break;
3005                        }
3006                        // `TODO`: if all factors are under target, return values
3007                    }
3008                    factors[i] = factor;
3009                }
3010            }
3011
3012            for _ in 0..fine_iterations {
3013                for i in 0..n {
3014                    let mut factor = factors[i];
3015                    let mut slope_positive = get_slope(dx, i, &mut values).is_sign_positive();
3016                    loop {
3017                        let slope = get_slope(dx, i, &mut values);
3018                        if slope.is_sign_positive() != slope_positive {
3019                            slope_positive = !slope_positive;
3020                            factor /= self.factor_decrease;
3021                        }
3022                        values[i] += Self::adjusted_slope(slope) * self.learn_rate * factor;
3023                        // println!(
3024                        // "Slope {slope} add {} log {} {factor}",
3025                        // adjusted_slope(slope) * self.learn_rate * factor,
3026                        // adjusted_slope(slope),
3027                        // );
3028                        if slope.abs() < target_accuracy {
3029                            break;
3030                        }
3031                        // `TODO`: if all factors are under target, return values
3032                    }
3033                    factors[i] = factor;
3034                }
3035            }
3036
3037            values
3038        }
3039    }
3040    impl SimultaneousOptions {
3041        #[inline(always)]
3042        fn adjusted_slope(n: f64) -> f64 {
3043            let n = n / 0.1;
3044            let ln = match n.partial_cmp(&0.) {
3045                Some(std::cmp::Ordering::Less) => -((-n + 1.).ln()),
3046                Some(std::cmp::Ordering::Greater) => (n + 1.).ln(),
3047                _ => 0.,
3048            };
3049            ln * 0.1
3050        }
3051        pub fn polynomial_optimization(
3052            &self,
3053            n: usize,
3054            fitness_function: impl Fn(&[f64]) -> f64,
3055        ) -> Vec<f64> {
3056            let mut values: Vec<f64> = std::iter::repeat(0.).take(n).collect();
3057            let mut factors: Vec<f64> = std::iter::repeat(1.).take(n).collect();
3058            let mut slopes: Vec<f64> = std::iter::repeat(0.).take(n).collect();
3059            let dx = 1e-11;
3060
3061            let get_slope = |dx: f64, i: usize, values: &mut [f64]| {
3062                let y1 = fitness_function(values);
3063                values[i] += dx;
3064                let y2 = fitness_function(values);
3065                values[i] -= dx;
3066                (y1 - y2) / dx
3067            };
3068
3069            let mut i = 0_usize;
3070            loop {
3071                i += 1;
3072                if i > 1_000_000 {
3073                    break;
3074                }
3075                for i in 0..n {
3076                    let slope = get_slope(dx, i, &mut values);
3077                    if slope.is_sign_positive() != slopes[i].is_sign_positive() {
3078                        if slope.abs() > slopes[i].abs() * 4.
3079                            && self.factor_decrease * self.factor_decrease
3080                                < (slope.abs() / slopes[i].abs())
3081                        {
3082                            factors[i] /= self
3083                                .factor_decrease
3084                                .max((slope.abs() / slopes[i].abs()).sqrt());
3085                        } else {
3086                            factors[i] /= self.factor_decrease;
3087                        }
3088                        factors[i] = factors[i].max(1e-6);
3089                    } else {
3090                        factors[i] *= self.factor_increase;
3091                    }
3092                    slopes[i] = slope;
3093                }
3094
3095                for i in 0..n {
3096                    let factor = factors[i];
3097                    let slope = slopes[i];
3098                    values[i] += Self::adjusted_slope(slope) * self.learn_rate * factor;
3099                    // println!(
3100                    // "{i} slope {slope} add {} {factor}",
3101                    // adjusted_log(slope) * self.learn_rate * factor,
3102                    // );
3103                }
3104                if i % 20 == 0 {
3105                    let mut v = 0.;
3106                    let mut factor = 1.;
3107                    for slope in &slopes {
3108                        v += slope.abs() * factor;
3109                        factor /= 5.0;
3110                    }
3111                    // println!("{v} {slopes:?}");
3112                    if v < self.target_accuracy {
3113                        break;
3114                    }
3115                }
3116            }
3117            values
3118        }
3119    }
3120
3121    impl PolynomialEstimator for ParallelOptions {
3122        fn model_polynomial(
3123            &self,
3124            predictors: &[f64],
3125            outcomes: &[f64],
3126            degree: usize,
3127        ) -> PolynomialCoefficients {
3128            PolynomialCoefficients::from(self.polynomial_optimization(degree + 1, 1e-6, |v| {
3129                -BorrowedPolynomial(v).determination_slice(predictors, outcomes)
3130            }))
3131        }
3132    }
3133    impl LinearEstimator for ParallelOptions {
3134        fn model_linear(&self, predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
3135            let v = self.polynomial_optimization(2, 1e-8, |v| {
3136                -LinearCoefficients { k: v[0], m: v[1] }.determination_slice(predictors, outcomes)
3137            });
3138            LinearCoefficients { k: v[0], m: v[1] }
3139        }
3140    }
3141    impl PolynomialEstimator for SimultaneousOptions {
3142        fn model_polynomial(
3143            &self,
3144            predictors: &[f64],
3145            outcomes: &[f64],
3146            degree: usize,
3147        ) -> PolynomialCoefficients {
3148            PolynomialCoefficients::from(self.polynomial_optimization(degree + 1, |v| {
3149                -BorrowedPolynomial(v).determination_slice(predictors, outcomes)
3150            }))
3151        }
3152    }
3153    impl LinearEstimator for SimultaneousOptions {
3154        fn model_linear(&self, predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
3155            let v = self.polynomial_optimization(2, |v| {
3156                -LinearCoefficients { k: v[0], m: v[1] }.determination_slice(predictors, outcomes)
3157            });
3158            LinearCoefficients { k: v[0], m: v[1] }
3159        }
3160    }
3161
3162    #[cfg(test)]
3163    mod tests {
3164        use super::*;
3165
3166        #[test]
3167        fn one_variable() {
3168            let now = std::time::Instant::now();
3169            let v = ParallelOptions::default()
3170                .polynomial_optimization(1, 1e-12, |values| (values[0] - 42.4242).powi(2));
3171            println!("{v:?} {:?}", now.elapsed());
3172        }
3173        #[test]
3174        fn two_variable_regression() {
3175            let now = std::time::Instant::now();
3176            let x = [1.3, 4.7, 9.4];
3177            let y = [4., 5.3, 6.7];
3178            let v = ParallelOptions::default().polynomial_optimization(2, 1e-6, |values| {
3179                -LinearCoefficients {
3180                    k: values[0],
3181                    m: values[1],
3182                }
3183                .determination_slice(&x, &y)
3184            });
3185            let coeffs = LinearCoefficients { k: v[0], m: v[1] };
3186            println!(
3187                "{coeffs} R² {} {:?}",
3188                coeffs.determination_slice(&x, &y),
3189                now.elapsed()
3190            );
3191        }
3192    }
3193}
3194
3195/// A random binary searching n-variable optimizer.
3196///
3197/// Independently binary searches the variables over the entire range of `f64`s.
3198/// Supports any number of variables to be optimized together.
3199///
3200/// This has performance in the ballpark of OLS, but enables you to give your own function,
3201/// which means you can optimize for things other than least squares along straight lines.
3202/// This opens up the opportunity to fit other functions (any you want) and
3203/// to use functions less prone to outliers (least squares is very prone).
3204pub mod binary_search {
3205    use super::*;
3206    #[cfg(feature = "binary_search_rng")]
3207    use rand::Rng;
3208    use std::borrow::Cow;
3209
3210    /// A trait which allows storage of n-variable optimization, either on the stack through arrays
3211    /// (`[f64; VARIABLE_COUNT]`) or allocated on the heap through `Vec`.
3212    #[allow(clippy::len_without_is_empty)] // just no
3213    pub trait NVariableStorage:
3214        std::ops::IndexMut<usize, Output = f64> + AsRef<[f64]> + AsMut<[f64]> + Clone
3215    {
3216        /// Associated data for use in construction of this type.
3217        /// The number of arguments in case of using a `Vec`,
3218        /// nothing when using arrays, as we know their length in
3219        /// compile time.
3220        type Data;
3221        /// The structure that's given to the fitness function.
3222        /// Needs to have the same length as the `.as_ref()` implementation of this struct.
3223        type Given<'a>: AsRef<[f64]>
3224        where
3225            Self: 'a;
3226        /// Creates a new storage filled with `num`.
3227        fn new_filled(data: &Self::Data, num: f64) -> Self;
3228        /// Borrow the current variables.
3229        fn borrow(&self) -> Self::Given<'_>;
3230    }
3231    impl<const LENGTH: usize> NVariableStorage for [f64; LENGTH] {
3232        type Data = ();
3233        type Given<'a> = [f64; LENGTH];
3234        fn new_filled(_data: &(), num: f64) -> Self {
3235            [num; LENGTH]
3236        }
3237        fn borrow(&self) -> Self::Given<'_> {
3238            *self
3239        }
3240    }
3241
3242    /// Dynamically sized storage, for use when the number of variables isn't known at compile
3243    /// time.
3244    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
3245    pub struct VariableLengthStorage(pub usize);
3246    impl NVariableStorage for Vec<f64> {
3247        type Data = VariableLengthStorage;
3248        type Given<'a> = &'a [f64];
3249        fn new_filled(data: &Self::Data, num: f64) -> Self {
3250            vec![num; data.0]
3251        }
3252        fn borrow(&self) -> Self::Given<'_> {
3253            self
3254        }
3255    }
3256    impl From<usize> for VariableLengthStorage {
3257        fn from(n: usize) -> Self {
3258            Self(n)
3259        }
3260    }
3261
3262    /// Generated using:
3263    /// ```
3264    /// (0..61)
3265    ///     .into_iter()
3266    ///     .map(|i| {
3267    ///         f64::MAX.powf(1. / (2.0f64.powi(i+2)))
3268    ///     })
3269    ///     .collect::<Vec<_>>();
3270    /// ```
3271    // braces for folding
3272    static SQRTS_FROM_F64_MAX: [f64; 61] = {
3273        [
3274            1.157920892373162e77,
3275            3.402823669209385e38,
3276            1.8446744073709552e19,
3277            4294967296.0,
3278            65536.0,
3279            256.0,
3280            16.0,
3281            4.0,
3282            2.0,
3283            core::f64::consts::SQRT_2,
3284            1.189207115002721,
3285            1.0905077326652577,
3286            1.0442737824274138,
3287            1.0218971486541166,
3288            1.0108892860517005,
3289            1.0054299011128027,
3290            1.0027112750502025,
3291            1.0013547198921082,
3292            1.0006771306930664,
3293            1.0003385080526823,
3294            1.0001692397053021,
3295            1.0000846162726944,
3296            1.0000423072413958,
3297            1.0000211533969647,
3298            1.0000105766425498,
3299            1.0000052883072919,
3300            1.0000026441501502,
3301            1.0000013220742012,
3302            1.0000006610368821,
3303            1.0000003305183864,
3304            1.0000001652591795,
3305            1.0000000826295863,
3306            1.0000000413147923,
3307            1.000000020657396,
3308            1.000000010328698,
3309            1.000000005164349,
3310            1.0000000025821745,
3311            1.0000000012910872,
3312            1.0000000006455436,
3313            1.0000000003227718,
3314            1.0000000001613858,
3315            1.000000000080693,
3316            1.0000000000403464,
3317            1.0000000000201732,
3318            1.0000000000100866,
3319            1.0000000000050433,
3320            1.0000000000025218,
3321            1.0000000000012608,
3322            1.0000000000006304,
3323            1.0000000000003153,
3324            1.0000000000001577,
3325            1.0000000000000788,
3326            1.0000000000000393,
3327            1.0000000000000198,
3328            1.0000000000000098,
3329            1.0000000000000049,
3330            1.0000000000000024,
3331            1.0000000000000013,
3332            1.0000000000000007,
3333            1.0000000000000002,
3334            1.0000000000000002,
3335        ]
3336    };
3337
3338    /// Options for the binary search optimization.
3339    #[derive(Debug, Clone, Copy, PartialEq)]
3340    pub struct Options {
3341        /// Number of iterations to search for the optimal value
3342        pub iterations: usize,
3343        /// How fine values you can get. 59 covers the whole range of `f64`
3344        /// 30 seems to get you ~7 significant digits
3345        pub precision: usize,
3346        /// The assumed max value. Use `f64::MAX` to cover the whole range of `f64`.
3347        pub max: f64,
3348        /// The factor for the randomness introduced when binary searching.
3349        /// Higher values result in finding more optimal values, but can also make it hard for the
3350        /// algorithm to find a good value.
3351        #[cfg(feature = "binary_search_rng")]
3352        pub randomness_factor: f64,
3353        /// Config for using [`random_subset_regression`].
3354        #[cfg(feature = "random_subset_regression")]
3355        pub random_subset_regression: Option<random_subset_regression::Config>,
3356    }
3357    impl Default for Options {
3358        fn default() -> Self {
3359            Self {
3360                iterations: 30,
3361                precision: 30,
3362                max: f64::MAX,
3363                #[cfg(feature = "binary_search_rng")]
3364                randomness_factor: 1.,
3365                #[cfg(feature = "random_subset_regression")]
3366                random_subset_regression: Some(Default::default()),
3367            }
3368        }
3369    }
3370    impl Options {
3371        /// Get max precision of every variable.
3372        pub fn max_precision(&self) -> Self {
3373            let mut me = *self;
3374            me.precision = 61;
3375            me
3376        }
3377
3378        /// Like [`Options::n_variable_optimization`] but without random variation. More easily
3379        /// falls into local maxima (a variables thought to be the best). Useful for independent
3380        /// variables.
3381        ///
3382        /// Faster than [`Options::n_variable_optimization`].
3383        pub fn n_variable_optimization_no_rng<NV: NVariableStorage>(
3384            &self,
3385            fitness_function: impl Fn(NV::Given<'_>) -> f64,
3386            data: NV::Data,
3387        ) -> NV {
3388            let initial_center = self.max.sqrt();
3389
3390            let mut values = NV::new_filled(&data, 0.);
3391            let factors = if self.max == f64::MAX {
3392                Cow::Borrowed(
3393                    &SQRTS_FROM_F64_MAX[0..(self.precision.min(SQRTS_FROM_F64_MAX.len()))],
3394                )
3395            } else {
3396                let mut f = initial_center;
3397                Cow::Owned(
3398                    (0..self.precision.min(61))
3399                        .map(|_| {
3400                            f = f.sqrt();
3401                            f
3402                        })
3403                        .collect::<Vec<_>>(),
3404                )
3405            };
3406            let n = values.as_ref().len();
3407
3408            for _ in 0..self.iterations {
3409                for i in 0..n {
3410                    let mut center = initial_center;
3411                    // for each precision level
3412                    for factor in factors.as_ref() {
3413                        // -1 to get 0 (the repeated division by sqrt approaches 1)
3414                        let center_over = center * factor;
3415                        let center_under = center / factor;
3416                        let value_over = center_over - 1.0;
3417                        let value_under = center_under - 1.0;
3418
3419                        let value_negative = -value_under;
3420                        values[i] = value_negative;
3421                        let fitness_negative = fitness_function(values.borrow());
3422
3423                        values[i] = value_over;
3424                        let fitness_over = fitness_function(values.borrow());
3425                        values[i] = value_under;
3426                        let fitness_under = fitness_function(values.borrow());
3427                        let best_current_sign = fitness_over.min(fitness_under);
3428
3429                        // if negative is optimal
3430                        if fitness_negative < best_current_sign {
3431                            values[i] = -value_over;
3432                            let fitness_negative_over = fitness_function(values.borrow());
3433                            values[i] = -value_under;
3434                            let fitness_negative_under = fitness_function(values.borrow());
3435
3436                            let best_opposite_sign =
3437                                fitness_negative_over.min(fitness_negative_under);
3438                            if best_opposite_sign < best_current_sign {
3439                                if fitness_negative_under < fitness_negative_over {
3440                                    center = -center_under;
3441                                // values[i] = -value_under already set
3442                                } else {
3443                                    center = -center_over;
3444                                    values[i] = -value_over;
3445                                }
3446                                continue;
3447                            }
3448                        }
3449
3450                        if !fitness_over.is_finite() || fitness_under < fitness_over {
3451                            center = center_under;
3452                            // values[i] = value_under already set
3453                        } else {
3454                            center = center_over;
3455                            values[i] = value_over;
3456                        }
3457                    }
3458                }
3459            }
3460            values
3461        }
3462        /// Optimize `n` variables to `fitness_function`.
3463        /// Will return a set of values which (hopefully) minimize `fitness_function`.
3464        #[cfg(feature = "binary_search_rng")]
3465        pub fn n_variable_optimization<NV: NVariableStorage>(
3466            &self,
3467            fitness_function: impl Fn(NV::Given<'_>) -> f64,
3468            data: NV::Data,
3469            rng: &mut impl Rng,
3470        ) -> NV {
3471            let initial_center = self.max.sqrt();
3472
3473            let mut values = NV::new_filled(&data, 0.);
3474            // pregenerate all successive sqrts of `initial_center`
3475            // we could instead do `factor = factor.sqrt()` at the end of each
3476            // `for _ in 0..self.precision`, but then, we'd do this `self.iterations` times.
3477            //
3478            // if `self.max` is f64::MAX, then use pregenerated list and don't allocate!
3479            let factors = if self.max == f64::MAX {
3480                Cow::Borrowed(
3481                    &SQRTS_FROM_F64_MAX[0..(self.precision.min(SQRTS_FROM_F64_MAX.len()))],
3482                )
3483            } else {
3484                let mut f = initial_center;
3485                Cow::Owned(
3486                    (0..self.precision.min(61))
3487                        .map(|_| {
3488                            f = f.sqrt();
3489                            f
3490                        })
3491                        .collect::<Vec<_>>(),
3492                )
3493            };
3494
3495            // track best values
3496            let mut best_fitness = f64::MAX;
3497            let mut best_values = values.clone();
3498
3499            let n = values.as_ref().len();
3500
3501            for iter in 0..self.iterations {
3502                // decrease randomness at the end
3503                let progress = 1.0 - iter as f64 / self.iterations as f64;
3504                // gen f32 since that takes less bytes
3505                let rng_factor =
3506                    1. + (2.0 * rng.gen::<f32>() as f64 - 1.) * self.randomness_factor * progress;
3507
3508                // for each variable to optimize
3509                for i in 0..n {
3510                    let mut center = initial_center;
3511                    // for each precision level (`for _ in 0..self.precision`, see note at definition
3512                    // of `factors`)
3513                    for factor in factors.as_ref() {
3514                        let factor = factor * rng_factor;
3515                        // -1 to get 0 (the repeated division by sqrt approaches 1)
3516                        let center_over = center * factor;
3517                        let center_under = center / factor;
3518                        let value_over = center_over - 1.0;
3519                        let value_under = center_under - 1.0;
3520
3521                        let value_negative = -value_under;
3522                        values[i] = value_negative;
3523                        let fitness_negative = fitness_function(values.borrow());
3524
3525                        values[i] = value_over;
3526                        let fitness_over = fitness_function(values.borrow());
3527                        // micro-optimization: we check the value_under last, so we don't have to
3528                        // set it again. This is optimal, because value_under is most likely
3529                        values[i] = value_under;
3530                        let fitness_under = fitness_function(values.borrow());
3531                        let best_current_sign = fitness_under.min(fitness_over);
3532
3533                        // if negative is optimal
3534                        if fitness_negative < best_current_sign {
3535                            values[i] = -value_over;
3536                            let fitness_negative_over = fitness_function(values.borrow());
3537                            values[i] = -value_under;
3538                            let fitness_negative_under = fitness_function(values.borrow());
3539
3540                            let best_opposite_sign =
3541                                fitness_negative_over.min(fitness_negative_under);
3542                            if best_opposite_sign < best_current_sign {
3543                                if fitness_negative_under < fitness_negative_over {
3544                                    center = -center_under;
3545                                // values[i] = -value_under already set
3546                                } else {
3547                                    center = -center_over;
3548                                    values[i] = -value_over;
3549                                }
3550                                continue;
3551                            }
3552                        }
3553                        if !fitness_over.is_finite() || fitness_under < fitness_over {
3554                            center = center_under;
3555                            // values[i] = value_under already set
3556                        } else {
3557                            center = center_over;
3558                            values[i] = value_over;
3559                        }
3560                    }
3561                }
3562
3563                // update best value
3564                let fitness = fitness_function(values.borrow());
3565                if fitness < best_fitness {
3566                    best_values.as_mut().copy_from_slice(values.as_ref());
3567                    best_fitness = fitness;
3568                }
3569            }
3570            best_values
3571        }
3572    }
3573
3574    #[cfg(feature = "binary_search_rng")]
3575    macro_rules! impl_estimator {
3576        ($(
3577            $name:ident, $method:ident, $ret:ident, $wrap:expr,
3578        )+) => {
3579            $(
3580                impl $name for Options {
3581                    fn $method(&self, predictors: &[f64], outcomes: &[f64]) -> $ret {
3582                        use rand::SeedableRng;
3583                        let mut rng = rand_xorshift::XorShiftRng::from_rng(rand::thread_rng()).unwrap();
3584
3585                        #[cfg(feature = "random_subset_regression")]
3586                        if let Some(random_config) = &self.random_subset_regression {
3587                            let subsets =
3588                                random_subset_regression::Subsets::new(
3589                                    predictors,
3590                                    outcomes,
3591                                    random_config,
3592                                    &mut rng
3593                                );
3594                            if let Some(subsets) = subsets {
3595                                return $wrap(self.n_variable_optimization(
3596                                    |model| {
3597                                        let (predictors, outcomes) = subsets.next_subset();
3598                                        -utils::manhattan_distance(
3599                                            &$wrap(model),
3600                                            predictors,
3601                                            outcomes,
3602                                        )
3603                                    },
3604                                    (),
3605                                    &mut rng,
3606                                ));
3607                            }
3608                        }
3609                        $wrap(self.n_variable_optimization(
3610                            #[inline(always)]
3611                            |model| -utils::manhattan_distance(&$wrap(model), predictors, outcomes),
3612                            (),
3613                            &mut rng,
3614                        ))
3615                    }
3616                }
3617            )+
3618        };
3619    }
3620    #[cfg(feature = "binary_search_rng")]
3621    macro_rules! impl_estimator_trig {
3622        ($(
3623            $name:ident, $method:ident, $ret:ident, $wrap:expr,
3624        )+) => {
3625            $(
3626                impl $name for Options {
3627                    fn $method(&self, predictors: &[f64], outcomes: &[f64], max_frequency: f64) -> $ret {
3628                        use rand::SeedableRng;
3629                        let mut rng = rand_xorshift::XorShiftRng::from_rng(rand::thread_rng()).unwrap();
3630
3631                        #[cfg(feature = "random_subset_regression")]
3632                        if let Some(random_config) = &self.random_subset_regression {
3633                            let subsets =
3634                                random_subset_regression::Subsets::new(
3635                                    predictors,
3636                                    outcomes,
3637                                    random_config,
3638                                    &mut rng
3639                                );
3640                            if let Some(subsets) = subsets {
3641                                return $wrap(self.n_variable_optimization(
3642                                    |model| {
3643                                        let (predictors, outcomes) = subsets.next_subset();
3644                                        -utils::trig_adjusted_manhattan_distance(
3645                                            &$wrap(model),
3646                                            model,
3647                                            predictors,
3648                                            outcomes,
3649                                            max_frequency,
3650                                        )
3651                                    },
3652                                    (),
3653                                    &mut rng,
3654                                ));
3655                            }
3656                        }
3657                        $wrap(self.n_variable_optimization(
3658                            #[inline(always)]
3659                            |model| {
3660                                -utils::trig_adjusted_manhattan_distance(
3661                                    &$wrap(model),
3662                                    model,
3663                                    predictors,
3664                                    outcomes,
3665                                    max_frequency
3666                                )
3667                            },
3668                            (),
3669                            &mut rng,
3670                        ))
3671                    }
3672                }
3673            )+
3674        };
3675    }
3676
3677    #[cfg(feature = "binary_search_rng")]
3678    impl_estimator!(
3679        LinearEstimator,
3680        model_linear,
3681        LinearCoefficients,
3682        utils::wrap_linear,
3683        //
3684        PowerEstimator,
3685        model_power,
3686        PowerCoefficients,
3687        utils::wrap_power,
3688        //
3689        ExponentialEstimator,
3690        model_exponential,
3691        ExponentialCoefficients,
3692        utils::wrap_exponential,
3693        //
3694        LogisticEstimator,
3695        model_logistic,
3696        LogisticCoefficients,
3697        utils::wrap_logistic,
3698    );
3699    #[cfg(feature = "binary_search_rng")]
3700    impl_estimator_trig!(
3701        SineEstimator,
3702        model_sine,
3703        SineCoefficients,
3704        SineCoefficients::wrap,
3705        //
3706        CosineEstimator,
3707        model_cosine,
3708        CosineCoefficients,
3709        CosineCoefficients::wrap,
3710        //
3711        TangentEstimator,
3712        model_tangent,
3713        TangentCoefficients,
3714        TangentCoefficients::wrap,
3715        //
3716        SecantEstimator,
3717        model_secant,
3718        SecantCoefficients,
3719        SecantCoefficients::wrap,
3720        //
3721        CosecantEstimator,
3722        model_cosecant,
3723        CosecantCoefficients,
3724        CosecantCoefficients::wrap,
3725        //
3726        CotangentEstimator,
3727        model_cotangent,
3728        CotangentCoefficients,
3729        CotangentCoefficients::wrap,
3730    );
3731    #[cfg(feature = "binary_search_rng")]
3732    impl PolynomialEstimator for Options {
3733        fn model_polynomial(
3734            &self,
3735            predictors: &[f64],
3736            outcomes: &[f64],
3737            degree: usize,
3738        ) -> PolynomialCoefficients {
3739            use rand::SeedableRng;
3740            let mut rng = rand_xorshift::XorShiftRng::from_rng(rand::thread_rng()).unwrap();
3741
3742            #[cfg(feature = "random_subset_regression")]
3743            if let Some(random_config) = &self.random_subset_regression {
3744                let subsets = random_subset_regression::Subsets::new(
3745                    predictors,
3746                    outcomes,
3747                    random_config,
3748                    &mut rng,
3749                );
3750                if let Some(subsets) = subsets {
3751                    return PolynomialCoefficients {
3752                        coefficients: (self.n_variable_optimization(
3753                            |model| {
3754                                let (predictors, outcomes) = subsets.next_subset();
3755                                -utils::manhattan_distance(
3756                                    &utils::BorrowedPolynomial(model),
3757                                    predictors,
3758                                    outcomes,
3759                                )
3760                            },
3761                            (degree + 1).into(),
3762                            &mut rng,
3763                        )),
3764                    };
3765                }
3766            }
3767            PolynomialCoefficients {
3768                coefficients: (self.n_variable_optimization(
3769                    #[inline(always)]
3770                    |model| {
3771                        -utils::manhattan_distance(
3772                            &utils::BorrowedPolynomial(model),
3773                            predictors,
3774                            outcomes,
3775                        )
3776                    },
3777                    (degree + 1).into(),
3778                    &mut rng,
3779                )),
3780            }
3781        }
3782    }
3783
3784    #[cfg(test)]
3785    mod tests {
3786        use super::super::*;
3787        use super::*;
3788
3789        #[test]
3790        fn one_variable_regression() {
3791            let now = std::time::Instant::now();
3792            let values = super::Options::default()
3793                .max_precision()
3794                .n_variable_optimization_no_rng::<[f64; 1]>(
3795                    |s| (s[0] - 42.42424242424242).abs(),
3796                    (),
3797                );
3798            println!("{values:?} {:?}", now.elapsed());
3799        }
3800        #[test]
3801        #[cfg(feature = "binary_search_rng")]
3802        fn two_variable_regression() {
3803            let mut rng = rand::thread_rng();
3804            let now = std::time::Instant::now();
3805            let x = [1.3, 4.7, 9.4];
3806            let y = [4., 5.3, 6.7];
3807            let v = Options::default().n_variable_optimization::<[f64; 2]>(
3808                |values| {
3809                    -utils::manhattan_distance(
3810                        &LinearCoefficients {
3811                            k: values[0],
3812                            m: values[1],
3813                        },
3814                        &x,
3815                        &y,
3816                    )
3817                },
3818                (),
3819                &mut rng,
3820            );
3821            let coeffs = LinearCoefficients { k: v[0], m: v[1] };
3822            println!(
3823                "{coeffs} R² {} {:?}",
3824                coeffs.determination_slice(&x, &y),
3825                now.elapsed()
3826            );
3827        }
3828        #[test]
3829        #[cfg(feature = "binary_search_rng")]
3830        fn second_degree_regression() {
3831            // init thread rng
3832            let _rng = rand::thread_rng();
3833            let now = std::time::Instant::now();
3834            let x = [1.3, 4.7, 9.4];
3835            let y = [4., 5.3, 6.7];
3836            let coeffs = Options::default().model_polynomial(&x, &y, 2);
3837            println!(
3838                "{coeffs} R² {} {:?}",
3839                coeffs.determination_slice(&x, &y),
3840                now.elapsed()
3841            );
3842        }
3843        #[test]
3844        #[cfg(feature = "binary_search_rng")]
3845        fn two_variable_optimization() {
3846            use rand::SeedableRng;
3847            // init thread rng
3848
3849            let mut rng = rand_xorshift::XorShiftRng::from_rng(rand::thread_rng()).unwrap();
3850            let now = std::time::Instant::now();
3851            let coeffs = Options::default()
3852                .max_precision()
3853                .n_variable_optimization::<[f64; 2]>(
3854                    |[v1, v2]| (v1 - 5.959).abs() + (v2 - (-234.234)).abs(),
3855                    (),
3856                    &mut rng,
3857                );
3858            println!("{coeffs:?} {:?}", now.elapsed());
3859        }
3860    }
3861}
3862
3863/// Improves speed of regression by only taking a few points into account.
3864///
3865/// Randomly selects several sets of points which are checked. Works with [`binary_search`]
3866/// and can easily be expanded to [`spiral`] and [`gradient_descent`].
3867#[cfg(feature = "random_subset_regression")]
3868#[allow(dead_code)] // the user interacts with this through `binary_search`, so when that's
3869                    // disabled, this becomes dead code.
3870pub mod random_subset_regression {
3871    use rand::prelude::Distribution;
3872    use rand::Rng;
3873    /// Config for generation of subsets of points.
3874    /// See [`super::binary_search::Options::random_subset_regression`].
3875    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
3876    pub struct Config {
3877        /// How many points each subset should contain.
3878        pub subset_length: usize,
3879        /// If [`Config::subset_length`] * this < the count of points, don't discard any points.
3880        /// If this is set to 1 (a panic will happen) the implementation would most likely pick
3881        /// several duplicate points.
3882        pub minimum_factor_of_length: usize,
3883        /// How many subsets to vary between
3884        pub subsets_count: usize,
3885    }
3886    impl Default for Config {
3887        fn default() -> Self {
3888            Self {
3889                subset_length: 100,
3890                minimum_factor_of_length: 4,
3891                subsets_count: 8,
3892            }
3893        }
3894    }
3895    pub(crate) struct Subsets {
3896        subsets: Vec<(Vec<f64>, Vec<f64>)>,
3897        i: std::rc::Rc<std::cell::RefCell<usize>>,
3898    }
3899    impl Subsets {
3900        pub(crate) fn new(
3901            x: &[f64],
3902            y: &[f64],
3903            config: &Config,
3904            rng: &mut impl Rng,
3905        ) -> Option<Self> {
3906            if x.len() != y.len() {
3907                return None;
3908            }
3909            if x.len() < config.subset_length * config.minimum_factor_of_length {
3910                return None;
3911            }
3912            if config.minimum_factor_of_length < 2 {
3913                eprintln!("random_subset_regression failed because configured `minimum_factor_of_length` is less than 2");
3914                return None;
3915            }
3916            if config.subsets_count < 2 {
3917                eprintln!(
3918                    "random_subset_regression failed because configured `subsets_count` is less than 2"
3919                );
3920                return None;
3921            }
3922            let distribution = rand::distributions::Uniform::new(0, x.len());
3923            let subsets = (0..config.subsets_count)
3924                .map(|_| {
3925                    let mut new_x = Vec::with_capacity(config.subset_length);
3926                    let mut new_y = Vec::with_capacity(config.subset_length);
3927                    for _ in 0..config.subset_length {
3928                        let idx = distribution.sample(rng);
3929                        new_x.push(x[idx]);
3930                        new_y.push(y[idx]);
3931                    }
3932                    (new_x, new_y)
3933                })
3934                .collect();
3935            Some(Self {
3936                subsets,
3937                i: std::rc::Rc::new(std::cell::RefCell::new(0)),
3938            })
3939        }
3940
3941        pub(crate) fn next_subset(&self) -> (&[f64], &[f64]) {
3942            let index = *self.i.borrow();
3943            let (predictors, outcomes) = &self.subsets[index];
3944            *self.i.borrow_mut() += 1;
3945            if index + 1 == self.subsets.len() {
3946                *self.i.borrow_mut() = 0;
3947            }
3948            (predictors, outcomes)
3949        }
3950    }
3951}
3952
3953mod utils {
3954    use super::*;
3955
3956    /// Like [`Determination::determination_slice`] but faster and more robust to outliers - values
3957    /// aren't squared (which increases the magnitude of outliers).
3958    ///
3959    /// `O(n)`
3960    #[inline(always)]
3961    pub(crate) fn manhattan_distance(
3962        model: &impl Predictive,
3963        predictors: &[f64],
3964        outcomes: &[f64],
3965    ) -> f64 {
3966        let mut error = 0.;
3967        for (predictor, outcome) in predictors.iter().copied().zip(outcomes.iter().copied()) {
3968            let predicted = model.predict_outcome(predictor);
3969            let length = (predicted - outcome).abs();
3970            error += length;
3971        }
3972
3973        -error
3974    }
3975
3976    pub(super) fn trig_adjusted_manhattan_distance(
3977        model: &impl Predictive,
3978        params: [f64; 3],
3979        predictors: &[f64],
3980        outcomes: &[f64],
3981        max_frequency: f64,
3982    ) -> f64 {
3983        let mut base = manhattan_distance(model, predictors, outcomes);
3984        if params[0].is_sign_negative()
3985            || params[1].is_sign_negative()
3986            || params[2].is_sign_negative()
3987        {
3988            base *= 10.;
3989        }
3990        if params[1] > max_frequency {
3991            base *= 10.;
3992        }
3993        base
3994    }
3995
3996    #[inline(always)]
3997    pub(super) fn wrap_linear(a: [f64; 2]) -> LinearCoefficients {
3998        LinearCoefficients { k: a[1], m: a[0] }
3999    }
4000    #[inline(always)]
4001    pub(super) fn wrap_power(a: [f64; 2]) -> PowerCoefficients {
4002        PowerCoefficients {
4003            e: a[1],
4004            k: a[0],
4005            predictor_additive: 0.,
4006            outcome_additive: 0.,
4007        }
4008    }
4009    #[inline(always)]
4010    pub(super) fn wrap_exponential(a: [f64; 2]) -> ExponentialCoefficients {
4011        ExponentialCoefficients {
4012            b: a[1],
4013            k: a[0],
4014            predictor_additive: 0.,
4015            outcome_additive: 0.,
4016        }
4017    }
4018    #[inline(always)]
4019    pub(super) fn wrap_logistic(a: [f64; 3]) -> LogisticCoefficients {
4020        LogisticCoefficients {
4021            x0: a[0],
4022            l: a[1],
4023            k: a[2],
4024        }
4025    }
4026    pub(super) struct BorrowedPolynomial<'a>(pub(super) &'a [f64]);
4027    impl<'a> Predictive for BorrowedPolynomial<'a> {
4028        #[inline(always)]
4029        fn predict_outcome(&self, predictor: f64) -> f64 {
4030            match self.0.len() {
4031                0 => 0.,
4032                1 => self.0[0],
4033                2 => self.0[1] * predictor + self.0[0],
4034                3 => self.0[2] * predictor * predictor + self.0[1] * predictor + self.0[0],
4035                4 => {
4036                    let p2 = predictor * predictor;
4037                    self.0[3] * p2 * predictor + self.0[2] * p2 + self.0[1] * predictor + self.0[0]
4038                }
4039                _ => {
4040                    let mut out = 0.0;
4041                    let mut pred = 1.;
4042                    for coefficient in self.0.iter().copied() {
4043                        out += pred * coefficient;
4044                        pred *= predictor;
4045                    }
4046                    out
4047                }
4048            }
4049        }
4050    }
4051}