Thu, 08 Dec 2022 14:10:07 +0200
Save more CSV files when iteration-wise plotting is enabled.
This helps to generate TikZ illustrations for presentations.
| 0 | 1 | |
| 2 | //! Implementation of the standard mollifier | |
| 3 | ||
| 4 | use rgsl::hypergeometric::hyperg_U; | |
| 5 | use float_extras::f64::{tgamma as gamma}; | |
| 6 | use numeric_literals::replace_float_literals; | |
| 7 | use serde::Serialize; | |
| 8 | use alg_tools::types::*; | |
| 9 | use alg_tools::euclidean::Euclidean; | |
| 10 | use alg_tools::norms::*; | |
| 11 | use alg_tools::loc::Loc; | |
| 12 | use alg_tools::sets::Cube; | |
| 13 | use alg_tools::bisection_tree::{ | |
| 14 | Support, | |
| 15 | Constant, | |
| 16 | Bounds, | |
| 17 | LocalAnalysis, | |
| 18 | GlobalAnalysis | |
| 19 | }; | |
| 20 | use alg_tools::mapping::Apply; | |
| 21 | use alg_tools::maputil::array_init; | |
| 22 | ||
| 23 | /// Reresentation of the (unnormalised) standard mollifier. | |
| 24 | /// | |
| 25 | /// For the `width` parameter $ε>0$, this is | |
| 26 | /// <div>$$ | |
| 27 | /// f(x)=\begin{cases} | |
| 28 | /// e^{\frac{ε^2}{\|x\|_2^2-ε^2}}, & \|x\|_2 < ε, \\ | |
| 29 | /// 0, & \text{otherwise}. | |
| 30 | /// \end{cases} | |
| 31 | /// $$</div> | |
| 32 | #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] | |
| 33 | pub struct Mollifier<C : Constant, const N : usize> { | |
| 34 | /// The parameter $ε$ of the mollifier. | |
| 35 | pub width : C, | |
| 36 | } | |
| 37 | ||
| 38 | #[replace_float_literals(C::Type::cast_from(literal))] | |
| 39 | impl<'a, C : Constant, const N : usize> Apply<&'a Loc<C::Type, N>> for Mollifier<C, N> { | |
| 40 | type Output = C::Type; | |
| 41 | #[inline] | |
| 42 | fn apply(&self, x : &'a Loc<C::Type, N>) -> Self::Output { | |
| 43 | let ε = self.width.value(); | |
| 44 | let ε2 = ε*ε; | |
| 45 | let n2 = x.norm2_squared(); | |
| 46 | if n2 < ε2 { | |
| 47 | (n2 / (n2 - ε2)).exp() | |
| 48 | } else { | |
| 49 | 0.0 | |
| 50 | } | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | impl<C : Constant, const N : usize> Apply<Loc<C::Type, N>> for Mollifier<C, N> { | |
| 55 | type Output = C::Type; | |
| 56 | #[inline] | |
| 57 | fn apply(&self, x : Loc<C::Type, N>) -> Self::Output { | |
| 58 | self.apply(&x) | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | impl<'a, C : Constant, const N : usize> Support<C::Type, N> for Mollifier<C, N> { | |
| 63 | #[inline] | |
| 64 | fn support_hint(&self) -> Cube<C::Type,N> { | |
| 65 | let ε = self.width.value(); | |
| 66 | array_init(|| [-ε, ε]).into() | |
| 67 | } | |
| 68 | ||
| 69 | #[inline] | |
| 70 | fn in_support(&self, x : &Loc<C::Type,N>) -> bool { | |
| 71 | x.norm2() < self.width.value() | |
| 72 | } | |
| 73 | ||
| 74 | /*fn fully_in_support(&self, _cube : &Cube<C::Type,N>) -> bool { | |
| 75 | todo!("Not implemented, but not used at the moment") | |
| 76 | }*/ | |
| 77 | } | |
| 78 | ||
| 79 | #[replace_float_literals(C::Type::cast_from(literal))] | |
| 80 | impl<'a, C : Constant, const N : usize> GlobalAnalysis<C::Type, Bounds<C::Type>> | |
| 81 | for Mollifier<C, N> { | |
| 82 | #[inline] | |
| 83 | fn global_analysis(&self) -> Bounds<C::Type> { | |
| 84 | // The function is maximised/minimised where the 2-norm is minimised/maximised. | |
| 85 | Bounds(0.0, 1.0) | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | impl<'a, C : Constant, const N : usize> LocalAnalysis<C::Type, Bounds<C::Type>, N> | |
| 90 | for Mollifier<C, N> { | |
| 91 | #[inline] | |
| 92 | fn local_analysis(&self, cube : &Cube<C::Type, N>) -> Bounds<C::Type> { | |
| 93 | // The function is maximised/minimised where the 2-norm is minimised/maximised. | |
| 94 | let lower = self.apply(cube.maxnorm_point()); | |
| 95 | let upper = self.apply(cube.minnorm_point()); | |
| 96 | Bounds(lower, upper) | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | /// Calculate integral of the standard mollifier of width 1 in $ℝ^n$. | |
| 101 | /// | |
| 102 | /// This is based on the formula from | |
| 103 | /// [https://math.stackexchange.com/questions/4359683/integral-of-the-usual-mollifier-function-finding-its-necessary-constant](). | |
| 104 | /// | |
| 105 | /// If `rescaled` is `true`, return the integral of the scaled mollifier that has value one at the | |
| 106 | /// origin. | |
| 107 | #[inline] | |
| 108 | pub fn mollifier_norm1(n_ : usize, rescaled : bool) -> f64 { | |
| 109 | assert!(n_ > 0); | |
| 110 | let n = n_ as f64; | |
| 111 | let q = 2.0; | |
| 112 | let p = 2.0; | |
| 113 | let base = (2.0*gamma(1.0 + 1.0/p)).powi(n_ as i32) | |
| 114 | /*/ gamma(1.0 + n / p) | |
| 115 | * gamma(1.0 + n / q)*/ | |
| 116 | * hyperg_U(1.0 + n / q, 2.0, 1.0); | |
| 117 | if rescaled { base } else { base / f64::E } | |
| 118 | } | |
| 119 | ||
| 120 | impl<'a, C : Constant, const N : usize> Norm<C::Type, L1> | |
| 121 | for Mollifier<C, N> { | |
| 122 | #[inline] | |
| 123 | fn norm(&self, _ : L1) -> C::Type { | |
| 124 | let ε = self.width.value(); | |
| 125 | C::Type::cast_from(mollifier_norm1(N, true)) * ε.powi(N as i32) | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | #[replace_float_literals(C::Type::cast_from(literal))] | |
| 130 | impl<'a, C : Constant, const N : usize> Norm<C::Type, Linfinity> | |
| 131 | for Mollifier<C, N> { | |
| 132 | #[inline] | |
| 133 | fn norm(&self, _ : Linfinity) -> C::Type { | |
| 134 | 1.0 | |
| 135 | } | |
| 136 | } |