|
1 //! Implementation of the hat function |
|
2 |
|
3 use numeric_literals::replace_float_literals; |
|
4 use serde::Serialize; |
|
5 use alg_tools::types::*; |
|
6 use alg_tools::norms::*; |
|
7 use alg_tools::loc::Loc; |
|
8 use alg_tools::sets::Cube; |
|
9 use alg_tools::bisection_tree::{ |
|
10 Support, |
|
11 Constant, |
|
12 Bounds, |
|
13 LocalAnalysis, |
|
14 GlobalAnalysis, |
|
15 Bounded, |
|
16 }; |
|
17 use alg_tools::mapping::Apply; |
|
18 use alg_tools::maputil::{array_init}; |
|
19 |
|
20 /// Representation of the hat function $f(x)=1-\\|x\\|\_1/ε$ of `width` $ε$ on $ℝ^N$. |
|
21 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] |
|
22 pub struct Hat<C : Constant, const N : usize> { |
|
23 /// The parameter $ε>0$. |
|
24 pub width : C, |
|
25 } |
|
26 |
|
27 #[replace_float_literals(C::Type::cast_from(literal))] |
|
28 impl<'a, C : Constant, const N : usize> Apply<&'a Loc<C::Type, N>> for Hat<C, N> { |
|
29 type Output = C::Type; |
|
30 #[inline] |
|
31 fn apply(&self, x : &'a Loc<C::Type, N>) -> Self::Output { |
|
32 let ε = self.width.value(); |
|
33 0.0.max(1.0-x.norm(L1)/ε) |
|
34 } |
|
35 } |
|
36 |
|
37 #[replace_float_literals(C::Type::cast_from(literal))] |
|
38 impl<C : Constant, const N : usize> Apply<Loc<C::Type, N>> for Hat<C, N> { |
|
39 type Output = C::Type; |
|
40 #[inline] |
|
41 fn apply(&self, x : Loc<C::Type, N>) -> Self::Output { |
|
42 self.apply(&x) |
|
43 } |
|
44 } |
|
45 |
|
46 |
|
47 #[replace_float_literals(C::Type::cast_from(literal))] |
|
48 impl<'a, C : Constant, const N : usize> Support<C::Type, N> for Hat<C, N> { |
|
49 #[inline] |
|
50 fn support_hint(&self) -> Cube<C::Type,N> { |
|
51 let ε = self.width.value(); |
|
52 array_init(|| [-ε, ε]).into() |
|
53 } |
|
54 |
|
55 #[inline] |
|
56 fn in_support(&self, x : &Loc<C::Type,N>) -> bool { |
|
57 x.norm(L1) < self.width.value() |
|
58 } |
|
59 |
|
60 /*fn fully_in_support(&self, _cube : &Cube<C::Type,N>) -> bool { |
|
61 todo!("Not implemented, but not used at the moment") |
|
62 }*/ |
|
63 |
|
64 #[inline] |
|
65 fn bisection_hint(&self, cube : &Cube<C::Type,N>) -> [Option<C::Type>; N] { |
|
66 let ε = self.width.value(); |
|
67 cube.map(|a, b| { |
|
68 if a < 1.0 { |
|
69 if 1.0 < b { |
|
70 Some(1.0) |
|
71 } else { |
|
72 if a < -ε { |
|
73 if b > -ε { Some(-ε) } else { None } |
|
74 } else { |
|
75 None |
|
76 } |
|
77 } |
|
78 } else { |
|
79 if b > ε { Some(ε) } else { None } |
|
80 } |
|
81 }); |
|
82 todo!("also diagonals") |
|
83 } |
|
84 } |
|
85 |
|
86 |
|
87 #[replace_float_literals(C::Type::cast_from(literal))] |
|
88 impl<'a, C : Constant, const N : usize> |
|
89 GlobalAnalysis<C::Type, Bounds<C::Type>> |
|
90 for Hat<C, N> { |
|
91 #[inline] |
|
92 fn global_analysis(&self) -> Bounds<C::Type> { |
|
93 Bounds(0.0, 1.0) |
|
94 } |
|
95 } |
|
96 |
|
97 impl<'a, C : Constant, const N : usize> |
|
98 LocalAnalysis<C::Type, Bounds<C::Type>, N> |
|
99 for Hat<C, N> { |
|
100 #[inline] |
|
101 fn local_analysis(&self, cube : &Cube<C::Type, N>) -> Bounds<C::Type> { |
|
102 // The function is maximised/minimised where the 1-norm is minimised/maximised. |
|
103 let lower = self.apply(cube.maxnorm_point()); |
|
104 let upper = self.apply(cube.minnorm_point()); |
|
105 Bounds(lower, upper) |
|
106 } |
|
107 } |
|
108 |
|
109 #[replace_float_literals(C::Type::cast_from(literal))] |
|
110 impl<'a, C : Constant, const N : usize> |
|
111 Norm<C::Type, Linfinity> |
|
112 for Hat<C, N> { |
|
113 #[inline] |
|
114 fn norm(&self, _ : Linfinity) -> C::Type { |
|
115 self.bounds().upper() |
|
116 } |
|
117 } |
|
118 |