1#![allow(clippy::type_complexity)]
2#![allow(dead_code)]
12
13use rand::RngExt as RandRng;
14use std::f64::consts::TAU;
15
16#[derive(Debug, Clone)]
24pub struct MeasurableSet {
25 pub label: String,
27 pub lower: Vec<f64>,
29 pub upper: Vec<f64>,
31}
32
33impl MeasurableSet {
34 pub fn new(label: impl Into<String>, lower: Vec<f64>, upper: Vec<f64>) -> Self {
39 assert_eq!(
40 lower.len(),
41 upper.len(),
42 "MeasurableSet: dimension mismatch"
43 );
44 MeasurableSet {
45 label: label.into(),
46 lower,
47 upper,
48 }
49 }
50
51 pub fn interval(a: f64, b: f64) -> Self {
53 Self::new(format!("[{a},{b}]"), vec![a], vec![b])
54 }
55
56 pub fn dim(&self) -> usize {
58 self.lower.len()
59 }
60
61 pub fn contains(&self, point: &[f64]) -> bool {
63 assert_eq!(
64 point.len(),
65 self.dim(),
66 "MeasurableSet::contains: dim mismatch"
67 );
68 self.lower
69 .iter()
70 .zip(self.upper.iter())
71 .zip(point.iter())
72 .all(|((lo, hi), x)| x >= lo && x <= hi)
73 }
74
75 pub fn is_null_set(&self) -> bool {
79 self.lower
80 .iter()
81 .zip(self.upper.iter())
82 .any(|(lo, hi)| (hi - lo).abs() < 1e-15)
83 }
84}
85
86pub trait Measure {
93 fn measure(&self, set: &MeasurableSet) -> f64;
95
96 fn is_null(&self, set: &MeasurableSet) -> bool {
98 self.measure(set).abs() < 1e-15
99 }
100
101 fn empty_set_measure(&self) -> f64 {
103 0.0
104 }
105
106 fn union_measure_disjoint(&self, a: &MeasurableSet, b: &MeasurableSet) -> f64 {
110 self.measure(a) + self.measure(b)
111 }
112}
113
114#[derive(Debug, Clone)]
123pub struct FiniteSigmaAlgebra {
124 pub n: usize,
126 sets: Vec<u64>,
128}
129
130impl FiniteSigmaAlgebra {
131 pub fn power_set(n: usize) -> Self {
136 assert!(n <= 63, "FiniteSigmaAlgebra: n too large (max 63)");
137 let sets = (0u64..(1u64 << n)).collect();
138 Self { n, sets }
139 }
140
141 pub fn trivial(n: usize) -> Self {
143 let full = if n == 0 { 0 } else { (1u64 << n) - 1 };
144 Self {
145 n,
146 sets: vec![0, full],
147 }
148 }
149
150 pub fn contains(&self, bitmask: u64) -> bool {
152 self.sets.contains(&bitmask)
153 }
154
155 pub fn cardinality(&self) -> usize {
157 self.sets.len()
158 }
159
160 pub fn is_closed_under_complement(&self) -> bool {
162 let full = if self.n == 0 { 0 } else { (1u64 << self.n) - 1 };
163 self.sets.iter().all(|&s| self.contains(full & !s))
164 }
165
166 pub fn is_closed_under_union(&self) -> bool {
168 for &a in &self.sets {
169 for &b in &self.sets {
170 if !self.contains(a | b) {
171 return false;
172 }
173 }
174 }
175 true
176 }
177}
178
179#[derive(Debug, Clone, Copy, Default)]
192pub struct LebesgueMeasure;
193
194impl Measure for LebesgueMeasure {
195 fn measure(&self, set: &MeasurableSet) -> f64 {
196 set.lower
197 .iter()
198 .zip(set.upper.iter())
199 .map(|(lo, hi)| (hi - lo).max(0.0))
200 .product()
201 }
202}
203
204impl LebesgueMeasure {
205 pub fn interval_measure(a: f64, b: f64) -> f64 {
207 (b - a).max(0.0)
208 }
209
210 pub fn box_measure(lower: &[f64], upper: &[f64]) -> f64 {
212 assert_eq!(lower.len(), upper.len(), "box_measure: dim mismatch");
213 lower
214 .iter()
215 .zip(upper.iter())
216 .map(|(lo, hi)| (hi - lo).max(0.0))
217 .product()
218 }
219
220 pub fn interval_intersection(a1: f64, a2: f64, b1: f64, b2: f64) -> f64 {
222 let lo = a1.max(b1);
223 let hi = a2.min(b2);
224 (hi - lo).max(0.0)
225 }
226
227 pub fn translation_invariant(a: f64, b: f64, t: f64) -> bool {
229 let original = Self::interval_measure(a, b);
230 let shifted = Self::interval_measure(a + t, b + t);
231 (original - shifted).abs() < 1e-12
232 }
233}
234
235pub struct ProbabilityMeasure {
245 pub density: Box<dyn Fn(&[f64]) -> f64>,
247 pub domain: MeasurableSet,
249 pub n_samples: usize,
251}
252
253impl ProbabilityMeasure {
254 pub fn new(
256 density: impl Fn(&[f64]) -> f64 + 'static,
257 domain: MeasurableSet,
258 n_samples: usize,
259 ) -> Self {
260 Self {
261 density: Box::new(density),
262 domain,
263 n_samples,
264 }
265 }
266
267 pub fn probability(&self, set: &MeasurableSet) -> f64 {
271 let mut rng = rand::rng();
272 let domain_vol = LebesgueMeasure.measure(&self.domain);
273 if domain_vol == 0.0 {
274 return 0.0;
275 }
276
277 let mut sum = 0.0;
278 let dim = self.domain.dim();
279 for _ in 0..self.n_samples {
280 let point: Vec<f64> = (0..dim)
281 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
282 .collect();
283 if set.contains(&point) {
284 sum += (self.density)(&point);
285 }
286 }
287 domain_vol * sum / self.n_samples as f64
288 }
289
290 pub fn total_mass(&self) -> f64 {
292 let mut rng = rand::rng();
293 let domain_vol = LebesgueMeasure.measure(&self.domain);
294 if domain_vol == 0.0 {
295 return 0.0;
296 }
297
298 let mut sum = 0.0;
299 let dim = self.domain.dim();
300 for _ in 0..self.n_samples {
301 let point: Vec<f64> = (0..dim)
302 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
303 .collect();
304 sum += (self.density)(&point);
305 }
306 domain_vol * sum / self.n_samples as f64
307 }
308
309 pub fn expectation(&self, g: &dyn Fn(&[f64]) -> f64) -> f64 {
311 let mut rng = rand::rng();
312 let domain_vol = LebesgueMeasure.measure(&self.domain);
313 if domain_vol == 0.0 {
314 return 0.0;
315 }
316
317 let mut numer = 0.0;
318 let mut denom = 0.0;
319 let dim = self.domain.dim();
320 for _ in 0..self.n_samples {
321 let point: Vec<f64> = (0..dim)
322 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
323 .collect();
324 let f_val = (self.density)(&point);
325 numer += f_val * g(&point);
326 denom += f_val;
327 }
328 if denom.abs() < 1e-15 {
329 return 0.0;
330 }
331 numer / denom
332 }
333
334 pub fn variance(&self, g: &dyn Fn(&[f64]) -> f64) -> f64 {
336 let eg = self.expectation(g);
337 let g2 = |x: &[f64]| g(x).powi(2);
338 let eg2 = self.expectation(&g2);
339 (eg2 - eg * eg).max(0.0)
340 }
341
342 pub fn entropy(&self) -> f64 {
344 let mut rng = rand::rng();
345 let domain_vol = LebesgueMeasure.measure(&self.domain);
346 if domain_vol == 0.0 {
347 return 0.0;
348 }
349
350 let mut sum = 0.0;
351 let dim = self.domain.dim();
352 for _ in 0..self.n_samples {
353 let point: Vec<f64> = (0..dim)
354 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
355 .collect();
356 let f_val = (self.density)(&point);
357 if f_val > 1e-15 {
358 sum += f_val * f_val.ln();
359 }
360 }
361 -domain_vol * sum / self.n_samples as f64
362 }
363}
364
365pub struct SignedMeasure {
376 pub positive_density: Box<dyn Fn(&[f64]) -> f64>,
378 pub negative_density: Box<dyn Fn(&[f64]) -> f64>,
380 pub domain: MeasurableSet,
382 pub n_samples: usize,
384}
385
386impl SignedMeasure {
387 pub fn new(
389 positive_density: impl Fn(&[f64]) -> f64 + 'static,
390 negative_density: impl Fn(&[f64]) -> f64 + 'static,
391 domain: MeasurableSet,
392 n_samples: usize,
393 ) -> Self {
394 Self {
395 positive_density: Box::new(positive_density),
396 negative_density: Box::new(negative_density),
397 domain,
398 n_samples,
399 }
400 }
401
402 pub fn from_density(
404 density: impl Fn(&[f64]) -> f64 + 'static,
405 domain: MeasurableSet,
406 n_samples: usize,
407 ) -> Self {
408 let density = std::rc::Rc::new(density);
409 let d1 = density.clone();
410 let d2 = density;
411 Self::new(
412 move |x| d1(x).max(0.0),
413 move |x| (-d2(x)).max(0.0),
414 domain,
415 n_samples,
416 )
417 }
418
419 pub fn evaluate(&self, set: &MeasurableSet) -> f64 {
421 let vol = LebesgueMeasure.measure(&self.domain);
422 if vol == 0.0 {
423 return 0.0;
424 }
425 let mut rng = rand::rng();
426 let mut sum = 0.0;
427 let dim = self.domain.dim();
428 for _ in 0..self.n_samples {
429 let point: Vec<f64> = (0..dim)
430 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
431 .collect();
432 if set.contains(&point) {
433 let fpos = (self.positive_density)(&point);
434 let fneg = (self.negative_density)(&point);
435 sum += fpos - fneg;
436 }
437 }
438 vol * sum / self.n_samples as f64
439 }
440
441 pub fn total_variation(&self, set: &MeasurableSet) -> f64 {
443 let vol = LebesgueMeasure.measure(&self.domain);
444 if vol == 0.0 {
445 return 0.0;
446 }
447 let mut rng = rand::rng();
448 let mut sum = 0.0;
449 let dim = self.domain.dim();
450 for _ in 0..self.n_samples {
451 let point: Vec<f64> = (0..dim)
452 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
453 .collect();
454 if set.contains(&point) {
455 let fpos = (self.positive_density)(&point);
456 let fneg = (self.negative_density)(&point);
457 sum += fpos + fneg;
458 }
459 }
460 vol * sum / self.n_samples as f64
461 }
462
463 pub fn hahn_positive_set_samples(&self, n: usize) -> Vec<Vec<f64>> {
467 let mut rng = rand::rng();
468 let dim = self.domain.dim();
469 let mut result = Vec::new();
470 for _ in 0..n {
471 let point: Vec<f64> = (0..dim)
472 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
473 .collect();
474 if (self.positive_density)(&point) > (self.negative_density)(&point) {
475 result.push(point);
476 }
477 }
478 result
479 }
480
481 pub fn jordan_decomposition(&self) -> (f64, f64) {
483 let vol = LebesgueMeasure.measure(&self.domain);
484 if vol == 0.0 {
485 return (0.0, 0.0);
486 }
487 let mut rng = rand::rng();
488 let mut pos_sum = 0.0;
489 let mut neg_sum = 0.0;
490 let dim = self.domain.dim();
491 for _ in 0..self.n_samples {
492 let point: Vec<f64> = (0..dim)
493 .map(|d| rng.random_range(self.domain.lower[d]..self.domain.upper[d]))
494 .collect();
495 pos_sum += (self.positive_density)(&point);
496 neg_sum += (self.negative_density)(&point);
497 }
498 let scale = vol / self.n_samples as f64;
499 (scale * pos_sum, scale * neg_sum)
500 }
501}
502
503#[derive(Debug, Clone)]
514pub struct ProductMeasure {
515 pub domain1: MeasurableSet,
517 pub domain2: MeasurableSet,
519}
520
521impl ProductMeasure {
522 pub fn new(domain1: MeasurableSet, domain2: MeasurableSet) -> Self {
524 Self { domain1, domain2 }
525 }
526
527 pub fn measure_product_box(&self, a: &MeasurableSet, b: &MeasurableSet) -> f64 {
531 LebesgueMeasure.measure(a) * LebesgueMeasure.measure(b)
532 }
533
534 pub fn product_domain(&self) -> MeasurableSet {
536 let mut lower = self.domain1.lower.clone();
537 lower.extend(self.domain2.lower.iter().copied());
538 let mut upper = self.domain1.upper.clone();
539 upper.extend(self.domain2.upper.iter().copied());
540 MeasurableSet::new("product_domain", lower, upper)
541 }
542
543 pub fn fubini_integrate(&self, f: &dyn Fn(f64, f64) -> f64, n_x: usize, n_y: usize) -> f64 {
547 assert_eq!(
548 self.domain1.dim(),
549 1,
550 "fubini_integrate: domain1 must be 1-D"
551 );
552 assert_eq!(
553 self.domain2.dim(),
554 1,
555 "fubini_integrate: domain2 must be 1-D"
556 );
557
558 let x0 = self.domain1.lower[0];
559 let x1 = self.domain1.upper[0];
560 let y0 = self.domain2.lower[0];
561 let y1 = self.domain2.upper[0];
562 let dx = (x1 - x0) / n_x as f64;
563 let dy = (y1 - y0) / n_y as f64;
564
565 let mut sum = 0.0;
566 for i in 0..n_x {
567 let x = x0 + (i as f64 + 0.5) * dx;
568 let inner: f64 = (0..n_y)
569 .map(|j| {
570 let y = y0 + (j as f64 + 0.5) * dy;
571 f(x, y)
572 })
573 .sum();
574 sum += inner * dy;
575 }
576 sum * dx
577 }
578
579 pub fn verify_fubini(&self, f: &dyn Fn(f64, f64) -> f64, n: usize) -> (f64, f64) {
581 let iterated = self.fubini_integrate(f, n, n);
582 let mut rng = rand::rng();
584 let x0 = self.domain1.lower[0];
585 let x1 = self.domain1.upper[0];
586 let y0 = self.domain2.lower[0];
587 let y1 = self.domain2.upper[0];
588 let vol = (x1 - x0) * (y1 - y0);
589 let mut mc_sum = 0.0;
590 for _ in 0..(n * n) {
591 let x = rng.random_range(x0..x1);
592 let y = rng.random_range(y0..y1);
593 mc_sum += f(x, y);
594 }
595 let mc = vol * mc_sum / (n * n) as f64;
596 (iterated, mc)
597 }
598}
599
600pub struct MeasureIntegral;
609
610impl MeasureIntegral {
611 pub fn integrate_1d(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
613 let h = (b - a) / n as f64;
614 (0..n)
615 .map(|i| {
616 let x = a + (i as f64 + 0.5) * h;
617 f(x) * h
618 })
619 .sum()
620 }
621
622 pub fn integrate_2d(
624 f: &dyn Fn(f64, f64) -> f64,
625 a: f64,
626 b: f64,
627 c: f64,
628 d: f64,
629 n: usize,
630 ) -> f64 {
631 let hx = (b - a) / n as f64;
632 let hy = (d - c) / n as f64;
633 let mut sum = 0.0;
634 for i in 0..n {
635 let x = a + (i as f64 + 0.5) * hx;
636 for j in 0..n {
637 let y = c + (j as f64 + 0.5) * hy;
638 sum += f(x, y);
639 }
640 }
641 sum * hx * hy
642 }
643
644 pub fn integrate_nd(
649 f: &dyn Fn(&[f64]) -> f64,
650 lower: &[f64],
651 upper: &[f64],
652 n_pts: usize,
653 ) -> f64 {
654 let dim = lower.len();
655 assert_eq!(dim, upper.len(), "integrate_nd: dim mismatch");
656 let steps: Vec<f64> = lower
657 .iter()
658 .zip(upper.iter())
659 .map(|(lo, hi)| (hi - lo) / n_pts as f64)
660 .collect();
661 let vol_cell: f64 = steps.iter().product();
662 let total_cells = n_pts.pow(dim as u32);
663
664 (0..total_cells)
665 .map(|idx| {
666 let mut point = vec![0.0f64; dim];
667 let mut rem = idx;
668 for d in 0..dim {
669 let coord = rem % n_pts;
670 rem /= n_pts;
671 point[d] = lower[d] + (coord as f64 + 0.5) * steps[d];
672 }
673 f(&point) * vol_cell
674 })
675 .sum()
676 }
677
678 pub fn monte_carlo(
682 f: &dyn Fn(&[f64]) -> f64,
683 lower: &[f64],
684 upper: &[f64],
685 n_samples: usize,
686 ) -> f64 {
687 let dim = lower.len();
688 assert_eq!(dim, upper.len(), "monte_carlo: dim mismatch");
689 let vol: f64 = lower
690 .iter()
691 .zip(upper.iter())
692 .map(|(lo, hi)| hi - lo)
693 .product();
694
695 let mut rng = rand::rng();
696 let mut sum = 0.0;
697 for _ in 0..n_samples {
698 let point: Vec<f64> = (0..dim)
699 .map(|d| rng.random_range(lower[d]..upper[d]))
700 .collect();
701 sum += f(&point);
702 }
703 vol * sum / n_samples as f64
704 }
705
706 pub fn importance_sampling(
711 f: &dyn Fn(f64) -> f64,
712 proposal_sample: &dyn Fn() -> f64,
713 proposal_density: &dyn Fn(f64) -> f64,
714 n_samples: usize,
715 ) -> f64 {
716 let mut sum = 0.0;
717 for _ in 0..n_samples {
718 let x = proposal_sample();
719 let g = proposal_density(x);
720 if g.abs() > 1e-15 {
721 sum += f(x) / g;
722 }
723 }
724 sum / n_samples as f64
725 }
726
727 pub fn riemann_stieltjes(
731 f: &dyn Fn(f64) -> f64,
732 g: &dyn Fn(f64) -> f64,
733 a: f64,
734 b: f64,
735 n: usize,
736 ) -> f64 {
737 let h = (b - a) / n as f64;
738 (0..n)
739 .map(|i| {
740 let x = a + i as f64 * h;
741 let xp = x + h;
742 f(x) * (g(xp) - g(x))
743 })
744 .sum()
745 }
746
747 pub fn monte_carlo_std_error(
749 f: &dyn Fn(&[f64]) -> f64,
750 lower: &[f64],
751 upper: &[f64],
752 n_samples: usize,
753 ) -> (f64, f64) {
754 let dim = lower.len();
755 let vol: f64 = lower
756 .iter()
757 .zip(upper.iter())
758 .map(|(lo, hi)| hi - lo)
759 .product();
760
761 let mut rng = rand::rng();
762 let mut vals = Vec::with_capacity(n_samples);
763 for _ in 0..n_samples {
764 let point: Vec<f64> = (0..dim)
765 .map(|d| rng.random_range(lower[d]..upper[d]))
766 .collect();
767 vals.push(f(&point));
768 }
769 let mean: f64 = vals.iter().sum::<f64>() / n_samples as f64;
770 let var: f64 =
771 vals.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / (n_samples - 1) as f64;
772 let std_err = (var / n_samples as f64).sqrt() * vol;
773 (vol * mean, std_err)
774 }
775}
776
777pub struct RadonNikodym;
788
789impl RadonNikodym {
790 pub fn estimate_at(x: f64, mu_samples: &[f64], nu_samples: &[f64], eps: f64) -> f64 {
795 let count =
796 |samples: &[f64]| samples.iter().filter(|&&s| (s - x).abs() <= eps).count() as f64;
797 let mu_count = count(mu_samples);
798 let nu_count = count(nu_samples);
799 if mu_count < 1e-15 {
800 return 0.0;
801 }
802 (nu_count / nu_samples.len() as f64) / (mu_count / mu_samples.len() as f64)
804 }
805}
806
807#[derive(Debug, Clone, Copy, PartialEq, Eq)]
813pub enum ConvergenceType {
814 AlmostEverywhere,
816 InMeasure,
818 InLp,
820 Uniform,
822}
823
824pub fn check_lp_convergence(
828 fn_seq: &[Box<dyn Fn(f64) -> f64>],
829 limit: &dyn Fn(f64) -> f64,
830 a: f64,
831 b: f64,
832 p: f64,
833 n_pts: usize,
834 tolerance: f64,
835) -> bool {
836 if fn_seq.is_empty() {
837 return true;
838 }
839 let h = (b - a) / n_pts as f64;
840 let last = fn_seq.last().expect("fn_seq is non-empty after check");
841 let lp_norm: f64 = (0..n_pts)
842 .map(|i| {
843 let x = a + (i as f64 + 0.5) * h;
844 (last(x) - limit(x)).abs().powf(p) * h
845 })
846 .sum::<f64>()
847 .powf(1.0 / p);
848 lp_norm < tolerance
849}
850
851pub struct OuterMeasure {
860 pub base: LebesgueMeasure,
862}
863
864impl OuterMeasure {
865 pub fn new() -> Self {
867 Self {
868 base: LebesgueMeasure,
869 }
870 }
871
872 pub fn estimate(&self, a: f64, b: f64, n_covers: usize) -> f64 {
874 let _ = n_covers;
876 (b - a).max(0.0)
877 }
878
879 pub fn verify_caratheodory(&self, a_lo: f64, a_hi: f64) -> bool {
882 let e_lo = 0.0;
883 let e_hi = 1.0;
884 let full = e_hi - e_lo;
885 let intersection = (a_hi.min(e_hi) - a_lo.max(e_lo)).max(0.0);
886 let complement = ((a_lo - e_lo).max(0.0) + (e_hi - a_hi).max(0.0)).min(full);
887 (full - (intersection + complement)).abs() < 1e-12
888 }
889}
890
891impl Default for OuterMeasure {
892 fn default() -> Self {
893 Self::new()
894 }
895}
896
897#[derive(Debug, Clone, Copy, Default)]
905pub struct CountingMeasure;
906
907impl CountingMeasure {
908 pub fn measure_set(set_size: usize) -> f64 {
910 set_size as f64
911 }
912
913 pub fn additivity(a_size: usize, b_size: usize) -> bool {
916 Self::measure_set(a_size + b_size) == Self::measure_set(a_size) + Self::measure_set(b_size)
917 }
918}
919
920pub struct ErgodicMeasure;
929
930impl ErgodicMeasure {
931 pub fn time_average(
935 f: &dyn Fn(f64) -> f64,
936 map: &dyn Fn(f64) -> f64,
937 x0: f64,
938 n: usize,
939 ) -> f64 {
940 let mut x = x0;
941 let mut sum = 0.0;
942 for _ in 0..n {
943 sum += f(x);
944 x = map(x);
945 }
946 sum / n as f64
947 }
948
949 pub fn birkhoff_check(
953 f: &dyn Fn(f64) -> f64,
954 map: &dyn Fn(f64) -> f64,
955 x0: f64,
956 space_average: f64,
957 n: usize,
958 tol: f64,
959 ) -> bool {
960 let ta = Self::time_average(f, map, x0, n);
961 (ta - space_average).abs() < tol
962 }
963}
964
965pub struct HaarMeasure;
973
974impl HaarMeasure {
975 pub fn integrate(f: &dyn Fn(f64) -> f64, n: usize) -> f64 {
977 use std::f64::consts::TAU;
978 let h = TAU / n as f64;
979 (0..n).map(|i| f(i as f64 * h) * h / TAU).sum()
980 }
981
982 pub fn translation_invariant(f: &dyn Fn(f64) -> f64, t: f64, n: usize) -> bool {
984 let original = Self::integrate(f, n);
985 let shifted = Self::integrate(&|theta| f((theta + t) % TAU), n);
986 (original - shifted).abs() < 1e-6
987 }
988}
989
990#[cfg(test)]
995mod tests {
996 use super::*;
997 use std::f64::consts::PI;
998
999 #[test]
1002 fn test_measurable_set_contains() {
1003 let s = MeasurableSet::interval(0.0, 1.0);
1004 assert!(s.contains(&[0.5]));
1005 assert!(!s.contains(&[1.5]));
1006 }
1007
1008 #[test]
1009 fn test_measurable_set_null() {
1010 let s = MeasurableSet::interval(1.0, 1.0);
1011 assert!(s.is_null_set());
1012 }
1013
1014 #[test]
1015 fn test_measurable_set_dim() {
1016 let s = MeasurableSet::new("box", vec![0.0, 0.0], vec![1.0, 1.0]);
1017 assert_eq!(s.dim(), 2);
1018 }
1019
1020 #[test]
1023 fn test_sigma_algebra_power_set_cardinality() {
1024 let sa = FiniteSigmaAlgebra::power_set(3);
1025 assert_eq!(sa.cardinality(), 8);
1026 }
1027
1028 #[test]
1029 fn test_sigma_algebra_trivial_cardinality() {
1030 let sa = FiniteSigmaAlgebra::trivial(4);
1031 assert_eq!(sa.cardinality(), 2);
1032 }
1033
1034 #[test]
1035 fn test_sigma_algebra_complement_closure() {
1036 let sa = FiniteSigmaAlgebra::power_set(3);
1037 assert!(sa.is_closed_under_complement());
1038 }
1039
1040 #[test]
1041 fn test_sigma_algebra_union_closure() {
1042 let sa = FiniteSigmaAlgebra::power_set(3);
1043 assert!(sa.is_closed_under_union());
1044 }
1045
1046 #[test]
1047 fn test_sigma_algebra_contains_empty() {
1048 let sa = FiniteSigmaAlgebra::power_set(4);
1049 assert!(sa.contains(0)); }
1051
1052 #[test]
1055 fn test_lebesgue_interval() {
1056 let m = LebesgueMeasure;
1057 let s = MeasurableSet::interval(0.0, 3.0);
1058 assert!((m.measure(&s) - 3.0).abs() < 1e-12);
1059 }
1060
1061 #[test]
1062 fn test_lebesgue_box() {
1063 let m = LebesgueMeasure;
1064 let s = MeasurableSet::new("box", vec![0.0, 0.0], vec![2.0, 3.0]);
1065 assert!((m.measure(&s) - 6.0).abs() < 1e-12);
1066 }
1067
1068 #[test]
1069 fn test_lebesgue_null_set() {
1070 let m = LebesgueMeasure;
1071 let s = MeasurableSet::interval(1.0, 1.0);
1072 assert!(m.is_null(&s));
1073 }
1074
1075 #[test]
1076 fn test_lebesgue_empty_set() {
1077 let m = LebesgueMeasure;
1078 assert!((m.empty_set_measure()).abs() < 1e-12);
1079 }
1080
1081 #[test]
1082 fn test_lebesgue_disjoint_union() {
1083 let m = LebesgueMeasure;
1084 let a = MeasurableSet::interval(0.0, 1.0);
1085 let b = MeasurableSet::interval(2.0, 4.0);
1086 let u = m.union_measure_disjoint(&a, &b);
1087 assert!((u - 3.0).abs() < 1e-12);
1088 }
1089
1090 #[test]
1091 fn test_lebesgue_translation_invariant() {
1092 assert!(LebesgueMeasure::translation_invariant(0.0, 1.0, 5.0));
1093 }
1094
1095 #[test]
1096 fn test_lebesgue_intersection() {
1097 let i = LebesgueMeasure::interval_intersection(0.0, 2.0, 1.0, 3.0);
1098 assert!((i - 1.0).abs() < 1e-12);
1099 }
1100
1101 #[test]
1102 fn test_lebesgue_no_intersection() {
1103 let i = LebesgueMeasure::interval_intersection(0.0, 1.0, 2.0, 3.0);
1104 assert!(i.abs() < 1e-12);
1105 }
1106
1107 #[test]
1110 fn test_probability_total_mass_uniform() {
1111 let pm = ProbabilityMeasure::new(|_x| 1.0, MeasurableSet::interval(0.0, 1.0), 10_000);
1113 let mass = pm.total_mass();
1114 assert!((mass - 1.0).abs() < 0.05, "total mass = {mass}");
1115 }
1116
1117 #[test]
1118 fn test_probability_expectation_linear() {
1119 let pm = ProbabilityMeasure::new(|_x| 1.0, MeasurableSet::interval(0.0, 1.0), 20_000);
1121 let ex = pm.expectation(&|x| x[0]);
1122 assert!((ex - 0.5).abs() < 0.05, "E[X] = {ex}");
1123 }
1124
1125 #[test]
1126 fn test_probability_variance_uniform() {
1127 let pm = ProbabilityMeasure::new(|_x| 1.0, MeasurableSet::interval(0.0, 1.0), 30_000);
1129 let vx = pm.variance(&|x| x[0]);
1130 assert!((vx - 1.0 / 12.0).abs() < 0.02, "Var[X] = {vx}");
1131 }
1132
1133 #[test]
1134 fn test_probability_entropy_positive() {
1135 let pm = ProbabilityMeasure::new(|_x| 1.0, MeasurableSet::interval(0.0, 1.0), 5_000);
1136 let h = pm.entropy();
1138 assert!(h.abs() < 0.05, "H = {h}");
1139 }
1140
1141 #[test]
1144 fn test_signed_measure_total_variation_positive() {
1145 let sm = SignedMeasure::new(|_| 2.0, |_| 1.0, MeasurableSet::interval(0.0, 1.0), 20_000);
1147 let tv = sm.total_variation(&MeasurableSet::interval(0.0, 1.0));
1148 assert!((tv - 3.0).abs() < 0.15, "TV = {tv}");
1149 }
1150
1151 #[test]
1152 fn test_signed_measure_evaluate() {
1153 let sm = SignedMeasure::new(|_| 1.0, |_| 0.0, MeasurableSet::interval(0.0, 1.0), 20_000);
1155 let v = sm.evaluate(&MeasurableSet::interval(0.0, 1.0));
1156 assert!((v - 1.0).abs() < 0.1, "ν = {v}");
1157 }
1158
1159 #[test]
1160 fn test_signed_measure_jordan_decomposition() {
1161 let sm = SignedMeasure::new(|_| 2.0, |_| 1.0, MeasurableSet::interval(0.0, 1.0), 20_000);
1162 let (pos, neg) = sm.jordan_decomposition();
1163 assert!((pos - 2.0).abs() < 0.15, "ν⁺ = {pos}");
1164 assert!((neg - 1.0).abs() < 0.15, "ν⁻ = {neg}");
1165 }
1166
1167 #[test]
1168 fn test_signed_measure_from_density() {
1169 let sm =
1171 SignedMeasure::from_density(|x| x[0] - 0.5, MeasurableSet::interval(0.0, 1.0), 20_000);
1172 let (pos, neg) = sm.jordan_decomposition();
1173 assert!(pos >= 0.0);
1174 assert!(neg >= 0.0);
1175 }
1176
1177 #[test]
1180 fn test_product_measure_box() {
1181 let pm = ProductMeasure::new(
1182 MeasurableSet::interval(0.0, 2.0),
1183 MeasurableSet::interval(0.0, 3.0),
1184 );
1185 let a = MeasurableSet::interval(0.0, 2.0);
1186 let b = MeasurableSet::interval(0.0, 3.0);
1187 let m = pm.measure_product_box(&a, &b);
1188 assert!((m - 6.0).abs() < 1e-12);
1189 }
1190
1191 #[test]
1192 fn test_fubini_constant_function() {
1193 let pm = ProductMeasure::new(
1194 MeasurableSet::interval(0.0, 1.0),
1195 MeasurableSet::interval(0.0, 1.0),
1196 );
1197 let result = pm.fubini_integrate(&|_x, _y| 1.0, 100, 100);
1199 assert!((result - 1.0).abs() < 0.01, "result = {result}");
1200 }
1201
1202 #[test]
1203 fn test_fubini_separable_function() {
1204 let pm = ProductMeasure::new(
1205 MeasurableSet::interval(0.0, 1.0),
1206 MeasurableSet::interval(0.0, 1.0),
1207 );
1208 let result = pm.fubini_integrate(&|x, y| x * y, 200, 200);
1210 assert!((result - 0.25).abs() < 0.01, "result = {result}");
1211 }
1212
1213 #[test]
1214 fn test_fubini_vs_monte_carlo() {
1215 let pm = ProductMeasure::new(
1216 MeasurableSet::interval(0.0, 1.0),
1217 MeasurableSet::interval(0.0, 1.0),
1218 );
1219 let (iterated, mc) = pm.verify_fubini(&|x, y| x * x + y * y, 50);
1220 assert!((iterated - mc).abs() < 0.1, "iterated={iterated} mc={mc}");
1221 }
1222
1223 #[test]
1226 fn test_integrate_1d_constant() {
1227 let result = MeasureIntegral::integrate_1d(&|_| 2.0, 0.0, 3.0, 1000);
1228 assert!((result - 6.0).abs() < 0.01, "result = {result}");
1229 }
1230
1231 #[test]
1232 fn test_integrate_1d_linear() {
1233 let result = MeasureIntegral::integrate_1d(&|x| x, 0.0, 1.0, 10_000);
1235 assert!((result - 0.5).abs() < 0.001, "result = {result}");
1236 }
1237
1238 #[test]
1239 fn test_integrate_1d_trig() {
1240 let result = MeasureIntegral::integrate_1d(&|x| x.sin(), 0.0, PI, 10_000);
1242 assert!((result - 2.0).abs() < 0.01, "result = {result}");
1243 }
1244
1245 #[test]
1246 fn test_integrate_2d_constant() {
1247 let result = MeasureIntegral::integrate_2d(&|_x, _y| 1.0, 0.0, 1.0, 0.0, 1.0, 100);
1249 assert!((result - 1.0).abs() < 0.01, "result = {result}");
1250 }
1251
1252 #[test]
1253 fn test_integrate_2d_separable() {
1254 let result = MeasureIntegral::integrate_2d(&|x, y| x * y, 0.0, 1.0, 0.0, 1.0, 200);
1256 assert!((result - 0.25).abs() < 0.005, "result = {result}");
1257 }
1258
1259 #[test]
1260 fn test_integrate_nd_volume() {
1261 let result =
1263 MeasureIntegral::integrate_nd(&|_| 1.0, &[0.0, 0.0, 0.0], &[1.0, 1.0, 1.0], 10);
1264 assert!((result - 1.0).abs() < 0.01, "result = {result}");
1265 }
1266
1267 #[test]
1268 fn test_monte_carlo_1d() {
1269 let result = MeasureIntegral::monte_carlo(&|x| x[0].powi(2), &[0.0], &[1.0], 50_000);
1271 assert!((result - 1.0 / 3.0).abs() < 0.02, "result = {result}");
1272 }
1273
1274 #[test]
1275 fn test_monte_carlo_2d() {
1276 let result =
1278 MeasureIntegral::monte_carlo(&|p| p[0] + p[1], &[0.0, 0.0], &[1.0, 1.0], 50_000);
1279 assert!((result - 1.0).abs() < 0.05, "result = {result}");
1280 }
1281
1282 #[test]
1283 fn test_riemann_stieltjes() {
1284 let result = MeasureIntegral::riemann_stieltjes(&|_| 1.0, &|x| x, 0.0, 1.0, 10_000);
1286 assert!((result - 1.0).abs() < 0.001, "result = {result}");
1287 }
1288
1289 #[test]
1290 fn test_monte_carlo_std_error() {
1291 let (est, err) = MeasureIntegral::monte_carlo_std_error(&|x| x[0], &[0.0], &[1.0], 10_000);
1292 assert!((est - 0.5).abs() < 0.05, "estimate = {est}");
1293 assert!(err > 0.0, "std error should be positive");
1294 }
1295
1296 #[test]
1299 fn test_radon_nikodym_estimate() {
1300 let samples: Vec<f64> = (0..1000).map(|i| i as f64 / 1000.0).collect();
1302 let rn = RadonNikodym::estimate_at(0.5, &samples, &samples, 0.05);
1303 assert!((rn - 1.0).abs() < 0.2, "RN derivative = {rn}");
1304 }
1305
1306 #[test]
1309 fn test_outer_measure_estimate() {
1310 let om = OuterMeasure::new();
1311 let m = om.estimate(0.0, 3.0, 10);
1312 assert!((m - 3.0).abs() < 1e-12);
1313 }
1314
1315 #[test]
1316 fn test_caratheodory_condition() {
1317 let om = OuterMeasure::new();
1318 assert!(om.verify_caratheodory(0.2, 0.7));
1319 }
1320
1321 #[test]
1324 fn test_counting_measure() {
1325 assert!((CountingMeasure::measure_set(5) - 5.0).abs() < 1e-12);
1326 assert!((CountingMeasure::measure_set(0)).abs() < 1e-12);
1327 }
1328
1329 #[test]
1332 fn test_ergodic_time_average_doubling_map() {
1333 let alpha = (5.0_f64.sqrt() - 1.0) / 2.0; let ta = ErgodicMeasure::time_average(
1337 &|x| x,
1338 &move |x| (x + alpha) % 1.0,
1339 0.123_456_789,
1340 200_000,
1341 );
1342 assert!((ta - 0.5).abs() < 0.05, "time average = {ta}");
1343 }
1344
1345 #[test]
1346 fn test_ergodic_birkhoff_check() {
1347 let alpha = (5.0_f64.sqrt() - 1.0) / 2.0;
1348 let ok = ErgodicMeasure::birkhoff_check(
1349 &|x| x,
1350 &move |x| (x + alpha) % 1.0,
1351 0.1234,
1352 0.5,
1353 200_000,
1354 0.05,
1355 );
1356 assert!(ok, "Birkhoff check failed");
1357 }
1358
1359 #[test]
1362 fn test_haar_measure_constant() {
1363 let result = HaarMeasure::integrate(&|_| 1.0, 1000);
1365 assert!((result - 1.0).abs() < 0.01, "result = {result}");
1366 }
1367
1368 #[test]
1369 fn test_haar_measure_cosine() {
1370 let result = HaarMeasure::integrate(&|theta: f64| theta.cos(), 10_000);
1372 assert!(result.abs() < 0.01, "result = {result}");
1373 }
1374
1375 #[test]
1376 fn test_haar_translation_invariant() {
1377 let ok = HaarMeasure::translation_invariant(&|theta: f64| theta.sin().powi(2), 1.0, 10_000);
1378 assert!(ok);
1379 }
1380
1381 #[test]
1384 fn test_lp_convergence() {
1385 let fns: Vec<Box<dyn Fn(f64) -> f64>> = (1..=10)
1387 .map(|n| {
1388 let b: Box<dyn Fn(f64) -> f64> = Box::new(move |x: f64| x.powf(1.0 / n as f64));
1389 b
1390 })
1391 .collect();
1392 let limit = |_x: f64| 1.0;
1393 let converges = check_lp_convergence(&fns, &limit, 0.0, 1.0, 2.0, 1000, 0.15);
1394 assert!(converges);
1395 }
1396}