1#![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
62pub struct DynModel {
64 model: Box<dyn Model>,
65}
66impl DynModel {
67 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
85pub trait Predictive {
87 fn predict_outcome(&self, predictor: f64) -> f64;
89 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}
103pub trait Determination: Predictive {
107 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 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 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
163pub 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 = "Model the [`"]
184 #[doc = stringify!($item)]
185 #[doc = "`] from `predictors` and `outcomes`."]
186 $($(#[$more_docs])*)?
187 fn $model(&self, predictors: &[f64], outcomes: &[f64], $($($arg: $ty),*)?) -> $item;
192 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 #[derive(Debug, Clone, Copy, PartialEq)]
212 pub struct LinearCoefficients {
213 pub k: f64,
215 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 #[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 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 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 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 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 #[derive(Debug, Clone, PartialEq)]
359 pub struct PowerCoefficients {
360 pub k: f64,
362 pub e: f64,
364 pub predictor_additive: f64,
368 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 #[derive(Debug, Clone, PartialEq)]
417 pub struct ExponentialCoefficients {
418 pub k: f64,
420 pub b: f64,
422 pub predictor_additive: f64,
426 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 #[derive(Debug, Clone, Copy, PartialEq)]
461 pub struct LogisticCoefficients {
462 pub x0: f64,
464 pub l: f64,
466 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 LinearEstimator -> LinearCoefficients, model_linear, boxed_linear
500
501 PolynomialEstimator -> PolynomialCoefficients,
503 (degree: usize), model_polynomial, boxed_polynomial
506
507 PowerEstimator -> PowerCoefficients, model_power, boxed_power
509
510 ExponentialEstimator -> ExponentialCoefficients, model_exponential, boxed_exponential
512
513 LogisticEstimator -> LogisticCoefficients, model_logistic, boxed_logistic
515 );
516
517 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 pub amplitude: f64,
544 pub frequency: f64,
546 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 SineCoefficients, f64::sin
585 CosineCoefficients, f64::cos
587 TangentCoefficients, f64::tan
589 );
590 simple_coefficients!(
591 SecantCoefficients,
593 1 / f64::sin, "sec"
594 CosecantCoefficients,
596 1 / f64::cos, "csc"
597 CotangentCoefficients,
599 1 / f64::tan, "cot"
600 );
601
602 estimator!(
603 SineEstimator -> SineCoefficients, (max_frequency: f64), model_sine, boxed_sine
605 CosineEstimator -> CosineCoefficients, (max_frequency: f64), model_cosine, boxed_cosine
607 TangentEstimator -> TangentCoefficients, (max_frequency: f64), model_tangent, boxed_tangent
609
610 SecantEstimator -> SecantCoefficients, (max_frequency: f64), model_secant, boxed_sesecant
612 CosecantEstimator -> CosecantCoefficients, (max_frequency: f64), model_cosecant, boxed_cosecant
614 CotangentEstimator -> CotangentCoefficients, (max_frequency: f64), model_cotangent, boxed_cotangent
616 );
617 }
618}
619
620pub fn best_fit(
643 predictors: &[f64],
644 outcomes: &[f64],
645 linear_estimator: &impl LinearEstimator,
646) -> DynModel {
647 const LINEAR_BUMP: f64 = 0.0;
650 const POWER_BUMP: f64 = 1.5;
652 const EXPONENTIAL_BUMP: f64 = 1.3;
654 #[allow(unused)]
659 const SECOND_DEGREE_DISADVANTAGE: f64 = 0.94;
660 #[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 #[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 best.unwrap().0
777}
778#[cfg(feature = "ols")]
780pub fn best_fit_ols(predictors: &[f64], outcomes: &[f64]) -> DynModel {
781 best_fit(predictors, outcomes, &OlsEstimator)
782}
783
784pub 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 #[cfg(feature = "ols")]
803 pub fn power_ols(predictors: &mut [f64], outcomes: &mut [f64]) -> PowerCoefficients {
804 power(predictors, outcomes, &OlsEstimator)
805 }
806 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 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 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 #[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 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 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 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#[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 pub static DEFAULT_PRECISION: RefCell<u32> = RefCell::new(256);
988 }
989 pub fn set_default_precision(new: u32) {
991 DEFAULT_PRECISION.with(|v| *v.borrow_mut() = new);
992 }
993 pub fn default_precision() -> u32 {
996 DEFAULT_PRECISION.with(|v| *v.borrow())
997 }
998 #[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 4
1260 }
1261 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, _max_ulps: u32) -> bool {
1262 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#[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 #[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 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 #[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 #[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 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 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
1803pub mod theil_sen {
1810 use super::*;
1811 use crate::{percentile, F64OrdHash};
1812 use std::fmt::Debug;
1813
1814 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 #[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 me.iters.push(i + usize::from(i + 1 < pairs) - 1);
1855 }
1856 #[allow(clippy::needless_range_loop)] 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 #[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 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 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 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 *unsafe { v.get_unchecked_mut(num) } = next;
1921 }
1922 *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 if self.s1.len() - *iter <= self.pairs - 1 - num {
1934 continue;
1935 }
1936
1937 #[allow(clippy::needless_range_loop)] for i in num + 1..self.pairs {
1941 let new = self.iters[i - 1] + usize::from(i + 1 < self.pairs);
1943 self.iters[i] = new;
1944 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 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 #[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 #[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 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 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 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 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 pub fn slow_linear(predictors: &[f64], outcomes: &[f64]) -> LinearCoefficients {
2058 assert_eq!(predictors.len(), outcomes.len());
2059 let median_slope = {
2062 let slopes = permutations(predictors, outcomes).map(|((x1, y1), (x2, y2))| {
2063 (y1 - y2) / (x1 - x2)
2065 });
2066 let mut slopes: Vec<_> = slopes.map(F64OrdHash).collect();
2067
2068 percentile::median(&mut slopes).resolve()
2069 };
2070
2071 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 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 {
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 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 coefficients.push(Vec::with_capacity(permutations_count))
2172 }
2173
2174 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 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 let intersect = y1 - x1 * slope;
2196
2197 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 2 => {
2208 while let Some(buf) = iter.next() {
2209 debug_assert_eq!(buf.len(), 3);
2210
2211 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 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 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 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 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
2409pub mod spiral {
2475 use super::*;
2476 use std::f64::consts::{E, TAU};
2477 use std::ops::Range;
2478 use utils::*;
2479
2480 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 last_best == best.0 .0 && i != 0 {
2524 return best.1;
2525 }
2526 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 }
2541 best.1
2542 }
2543 pub fn three_variable_optimization(
2552 fitness_function: impl Fn([f64; 3]) -> f64,
2553 options: Options,
2554 ) -> [f64; 3] {
2555 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 #[must_use]
2640 #[derive(Debug, Clone, PartialEq)]
2641 pub struct Options {
2642 pub exponent_coefficient: f64,
2646 pub angle_coefficient: f64,
2648 pub num_lockon: usize,
2650 pub samples_per_rotation: f64,
2652 pub range: Range<f64>,
2654 pub turns: f64,
2658 }
2659 impl Options {
2660 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 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 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 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 PowerEstimator,
2780 model_power,
2781 two_variable_optimization,
2782 PowerCoefficients,
2783 wrap_power,
2784 ExponentialEstimator,
2786 model_exponential,
2787 two_variable_optimization,
2788 ExponentialCoefficients,
2789 wrap_exponential,
2790 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 CosineEstimator,
2805 model_cosine,
2806 three_variable_optimization,
2807 CosineCoefficients,
2808 CosineCoefficients::wrap,
2809 TangentEstimator,
2811 model_tangent,
2812 three_variable_optimization,
2813 TangentCoefficients,
2814 TangentCoefficients::wrap,
2815 SecantEstimator,
2817 model_secant,
2818 three_variable_optimization,
2819 SecantCoefficients,
2820 SecantCoefficients::wrap,
2821 CosecantEstimator,
2823 model_cosecant,
2824 three_variable_optimization,
2825 CosecantCoefficients,
2826 CosecantCoefficients::wrap,
2827 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 #[derive(Debug, Clone, PartialEq)]
2866 pub struct SpiralLogisticWithCeiling {
2867 pub opts: Options,
2869 pub ceiling: f64,
2872 }
2873
2874 impl SpiralLogisticWithCeiling {
2875 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#[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 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 }
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 if slope.abs() < target_accuracy {
3029 break;
3030 }
3031 }
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 }
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 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
3195pub mod binary_search {
3205 use super::*;
3206 #[cfg(feature = "binary_search_rng")]
3207 use rand::Rng;
3208 use std::borrow::Cow;
3209
3210 #[allow(clippy::len_without_is_empty)] pub trait NVariableStorage:
3214 std::ops::IndexMut<usize, Output = f64> + AsRef<[f64]> + AsMut<[f64]> + Clone
3215 {
3216 type Data;
3221 type Given<'a>: AsRef<[f64]>
3224 where
3225 Self: 'a;
3226 fn new_filled(data: &Self::Data, num: f64) -> Self;
3228 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 #[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 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 #[derive(Debug, Clone, Copy, PartialEq)]
3340 pub struct Options {
3341 pub iterations: usize,
3343 pub precision: usize,
3346 pub max: f64,
3348 #[cfg(feature = "binary_search_rng")]
3352 pub randomness_factor: f64,
3353 #[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 pub fn max_precision(&self) -> Self {
3373 let mut me = *self;
3374 me.precision = 61;
3375 me
3376 }
3377
3378 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 factor in factors.as_ref() {
3413 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 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 } 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 } else {
3454 center = center_over;
3455 values[i] = value_over;
3456 }
3457 }
3458 }
3459 }
3460 values
3461 }
3462 #[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 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 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 let progress = 1.0 - iter as f64 / self.iterations as f64;
3504 let rng_factor =
3506 1. + (2.0 * rng.gen::<f32>() as f64 - 1.) * self.randomness_factor * progress;
3507
3508 for i in 0..n {
3510 let mut center = initial_center;
3511 for factor in factors.as_ref() {
3514 let factor = factor * rng_factor;
3515 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 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 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 } 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 } else {
3557 center = center_over;
3558 values[i] = value_over;
3559 }
3560 }
3561 }
3562
3563 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 PowerEstimator,
3685 model_power,
3686 PowerCoefficients,
3687 utils::wrap_power,
3688 ExponentialEstimator,
3690 model_exponential,
3691 ExponentialCoefficients,
3692 utils::wrap_exponential,
3693 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 CosineEstimator,
3707 model_cosine,
3708 CosineCoefficients,
3709 CosineCoefficients::wrap,
3710 TangentEstimator,
3712 model_tangent,
3713 TangentCoefficients,
3714 TangentCoefficients::wrap,
3715 SecantEstimator,
3717 model_secant,
3718 SecantCoefficients,
3719 SecantCoefficients::wrap,
3720 CosecantEstimator,
3722 model_cosecant,
3723 CosecantCoefficients,
3724 CosecantCoefficients::wrap,
3725 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 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 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#[cfg(feature = "random_subset_regression")]
3868#[allow(dead_code)] pub mod random_subset_regression {
3871 use rand::prelude::Distribution;
3872 use rand::Rng;
3873 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
3876 pub struct Config {
3877 pub subset_length: usize,
3879 pub minimum_factor_of_length: usize,
3883 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 #[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}