| |
1 /*! |
| |
2 Bounded and minimizable/maximizable mappings. |
| |
3 */ |
| |
4 |
| |
5 use crate::instance::{Instance, Space}; |
| |
6 use crate::mapping::Mapping; |
| |
7 use crate::sets::{Cube, Set}; |
| |
8 use crate::types::{Float, Num}; |
| |
9 |
| |
10 /// Trait for globally analysing a property `A` of a [`Mapping`]. |
| |
11 /// |
| |
12 /// Typically `A` is an [`Aggregator`][super::bisection_tree::Aggregator] |
| |
13 /// such as [`Bounds`]. |
| |
14 pub trait GlobalAnalysis<F: Num, A> { |
| |
15 /// Perform global analysis of the property `A` of `Self`. |
| |
16 /// |
| |
17 /// As an example, in the case of `A` being [`Bounds`], |
| |
18 /// this function will return global upper and lower bounds for the mapping |
| |
19 /// represented by `self`. |
| |
20 fn global_analysis(&self) -> A; |
| |
21 } |
| |
22 |
| |
23 // default impl<F, A, N, L> GlobalAnalysis<F, A, N> for L |
| |
24 // where L : LocalAnalysis<F, A, N> { |
| |
25 // #[inline] |
| |
26 // fn global_analysis(&self) -> Bounds<F> { |
| |
27 // self.local_analysis(&self.support_hint()) |
| |
28 // } |
| |
29 // } |
| |
30 |
| |
31 /// Trait for locally analysing a property `A` of a [`Mapping`] (implementing [`super::bisection_tree::Support`]) |
| |
32 /// within a [`Cube`]. |
| |
33 /// |
| |
34 /// Typically `A` is an [`Aggregator`][super::bisection_tree::Aggregator] such as [`Bounds`]. |
| |
35 pub trait LocalAnalysis<F: Num, A, const N: usize>: GlobalAnalysis<F, A> { |
| |
36 /// Perform local analysis of the property `A` of `Self`. |
| |
37 /// |
| |
38 /// As an example, in the case of `A` being [`Bounds`], |
| |
39 /// this function will return upper and lower bounds within `cube` for the mapping |
| |
40 /// represented by `self`. |
| |
41 fn local_analysis(&self, cube: &Cube<N, F>) -> A; |
| |
42 } |
| |
43 |
| |
44 /// Trait for determining the upper and lower bounds of an float-valued [`Mapping`]. |
| |
45 /// |
| |
46 /// This is a blanket-implemented alias for [`GlobalAnalysis`]`<F, Bounds<F>>` |
| |
47 /// [`Mapping`] is not a supertrait to allow flexibility in the implementation of either |
| |
48 /// reference or non-reference arguments. |
| |
49 pub trait Bounded<F: Float>: GlobalAnalysis<F, Bounds<F>> { |
| |
50 /// Return lower and upper bounds for the values of of `self`. |
| |
51 #[inline] |
| |
52 fn bounds(&self) -> Bounds<F> { |
| |
53 self.global_analysis() |
| |
54 } |
| |
55 } |
| |
56 |
| |
57 impl<F: Float, T: GlobalAnalysis<F, Bounds<F>>> Bounded<F> for T {} |
| |
58 |
| |
59 /// A real-valued [`Mapping`] that provides rough bounds as well as minimisation and maximisation. |
| |
60 pub trait MinMaxMapping<Domain: Space, F: Float = f64>: |
| |
61 Mapping<Domain, Codomain = F> + Bounded<F> |
| |
62 { |
| |
63 /// Maximise the mapping within stated value `tolerance`. |
| |
64 /// |
| |
65 /// At most `max_steps` refinement steps are taken. |
| |
66 /// Returns the approximate maximiser and the corresponding function value. |
| |
67 fn maximise(&mut self, tolerance: F, max_steps: usize) -> (Domain, F); |
| |
68 |
| |
69 /// Maximise the mapping within stated value `tolerance` subject to a lower bound. |
| |
70 /// |
| |
71 /// At most `max_steps` refinement steps are taken. |
| |
72 /// Returns the approximate maximiser and the corresponding function value when one is found |
| |
73 /// above the `bound` threshold, otherwise `None`. |
| |
74 fn maximise_above(&mut self, bound: F, tolerance: F, max_steps: usize) -> Option<(Domain, F)> { |
| |
75 let res @ (_, v) = self.maximise(tolerance, max_steps); |
| |
76 (v > bound).then_some(res) |
| |
77 } |
| |
78 |
| |
79 /// Minimise the mapping within stated value `tolerance`. |
| |
80 /// |
| |
81 /// At most `max_steps` refinement steps are taken. |
| |
82 /// Returns the approximate minimiser and the corresponding function value. |
| |
83 fn minimise(&mut self, tolerance: F, max_steps: usize) -> (Domain, F); |
| |
84 |
| |
85 /// Minimise the mapping within stated value `tolerance` subject to a lower bound. |
| |
86 /// |
| |
87 /// At most `max_steps` refinement steps are taken. |
| |
88 /// Returns the approximate minimiser and the corresponding function value when one is found |
| |
89 /// below the `bound` threshold, otherwise `None`. |
| |
90 fn minimise_below(&mut self, bound: F, tolerance: F, max_steps: usize) -> Option<(Domain, F)> { |
| |
91 let res @ (_, v) = self.minimise(tolerance, max_steps); |
| |
92 (v < bound).then_some(res) |
| |
93 } |
| |
94 |
| |
95 /// Verify that the mapping has a given upper `bound` within indicated `tolerance`. |
| |
96 /// |
| |
97 /// At most `max_steps` refinement steps are taken. |
| |
98 fn has_upper_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { |
| |
99 match self.maximise_above(bound, tolerance, max_steps) { |
| |
100 None => true, |
| |
101 Some((_, v)) => v <= bound, |
| |
102 } |
| |
103 } |
| |
104 |
| |
105 /// Verify that the mapping has a given lower `bound` within indicated `tolerance`. |
| |
106 /// |
| |
107 /// At most `max_steps` refinement steps are taken. |
| |
108 fn has_lower_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { |
| |
109 match self.minimise_below(bound, tolerance, max_steps) { |
| |
110 None => true, |
| |
111 Some((_, v)) => v >= bound, |
| |
112 } |
| |
113 } |
| |
114 } |
| |
115 |
| |
116 /// Upper and lower bounds on an `F`-valued function. |
| |
117 #[derive(Copy, Clone, Debug)] |
| |
118 pub struct Bounds<F>( |
| |
119 /// Lower bound |
| |
120 pub F, |
| |
121 /// Upper bound |
| |
122 pub F, |
| |
123 ); |
| |
124 |
| |
125 impl<F: Copy> Bounds<F> { |
| |
126 /// Returns the lower bound |
| |
127 #[inline] |
| |
128 pub fn lower(&self) -> F { |
| |
129 self.0 |
| |
130 } |
| |
131 |
| |
132 /// Returns the upper bound |
| |
133 #[inline] |
| |
134 pub fn upper(&self) -> F { |
| |
135 self.1 |
| |
136 } |
| |
137 } |
| |
138 |
| |
139 impl<F: Float> Bounds<F> { |
| |
140 /// Returns a uniform bound. |
| |
141 /// |
| |
142 /// This is maximum over the absolute values of the upper and lower bound. |
| |
143 #[inline] |
| |
144 pub fn uniform(&self) -> F { |
| |
145 let &Bounds(lower, upper) = self; |
| |
146 lower.abs().max(upper.abs()) |
| |
147 } |
| |
148 |
| |
149 /// Construct a bounds, making sure `lower` bound is less than `upper` |
| |
150 #[inline] |
| |
151 pub fn corrected(lower: F, upper: F) -> Self { |
| |
152 if lower <= upper { |
| |
153 Bounds(lower, upper) |
| |
154 } else { |
| |
155 Bounds(upper, lower) |
| |
156 } |
| |
157 } |
| |
158 |
| |
159 /// Refine the lower bound |
| |
160 #[inline] |
| |
161 pub fn refine_lower(&self, lower: F) -> Self { |
| |
162 let &Bounds(l, u) = self; |
| |
163 debug_assert!(l <= u); |
| |
164 Bounds(l.max(lower), u.max(lower)) |
| |
165 } |
| |
166 |
| |
167 /// Refine the lower bound |
| |
168 #[inline] |
| |
169 pub fn refine_upper(&self, upper: F) -> Self { |
| |
170 let &Bounds(l, u) = self; |
| |
171 debug_assert!(l <= u); |
| |
172 Bounds(l.min(upper), u.min(upper)) |
| |
173 } |
| |
174 } |
| |
175 |
| |
176 impl<'a, F: Float> std::ops::Add<Self> for Bounds<F> { |
| |
177 type Output = Self; |
| |
178 #[inline] |
| |
179 fn add(self, Bounds(l2, u2): Self) -> Self::Output { |
| |
180 let Bounds(l1, u1) = self; |
| |
181 debug_assert!(l1 <= u1 && l2 <= u2); |
| |
182 Bounds(l1 + l2, u1 + u2) |
| |
183 } |
| |
184 } |
| |
185 |
| |
186 impl<'a, F: Float> std::ops::Mul<Self> for Bounds<F> { |
| |
187 type Output = Self; |
| |
188 #[inline] |
| |
189 fn mul(self, Bounds(l2, u2): Self) -> Self::Output { |
| |
190 let Bounds(l1, u1) = self; |
| |
191 debug_assert!(l1 <= u1 && l2 <= u2); |
| |
192 let a = l1 * l2; |
| |
193 let b = u1 * u2; |
| |
194 // The order may flip when negative numbers are involved, so need min/max |
| |
195 Bounds(a.min(b), a.max(b)) |
| |
196 } |
| |
197 } |
| |
198 |
| |
199 impl<F: Float> std::iter::Product for Bounds<F> { |
| |
200 #[inline] |
| |
201 fn product<I>(mut iter: I) -> Self |
| |
202 where |
| |
203 I: Iterator<Item = Self>, |
| |
204 { |
| |
205 match iter.next() { |
| |
206 None => Bounds(F::ZERO, F::ZERO), |
| |
207 Some(init) => iter.fold(init, |a, b| a * b), |
| |
208 } |
| |
209 } |
| |
210 } |
| |
211 |
| |
212 impl<F: Float> Set<F> for Bounds<F> { |
| |
213 fn contains<I: Instance<F>>(&self, item: I) -> bool { |
| |
214 let v = item.own(); |
| |
215 let &Bounds(l, u) = self; |
| |
216 debug_assert!(l <= u); |
| |
217 l <= v && v <= u |
| |
218 } |
| |
219 } |
| |
220 |
| |
221 impl<F: Float> Bounds<F> { |
| |
222 /// Calculate a common bound (glb, lub) for two bounds. |
| |
223 #[inline] |
| |
224 pub fn common(&self, &Bounds(l2, u2): &Self) -> Self { |
| |
225 let &Bounds(l1, u1) = self; |
| |
226 debug_assert!(l1 <= u1 && l2 <= u2); |
| |
227 Bounds(l1.min(l2), u1.max(u2)) |
| |
228 } |
| |
229 |
| |
230 /// Indicates whether `Self` is a superset of the argument bound. |
| |
231 #[inline] |
| |
232 pub fn superset(&self, &Bounds(l2, u2): &Self) -> bool { |
| |
233 let &Bounds(l1, u1) = self; |
| |
234 debug_assert!(l1 <= u1 && l2 <= u2); |
| |
235 l1 <= l2 && u2 <= u1 |
| |
236 } |
| |
237 |
| |
238 /// Returns the greatest bound contained by both argument bounds, if one exists. |
| |
239 #[inline] |
| |
240 pub fn glb(&self, &Bounds(l2, u2): &Self) -> Option<Self> { |
| |
241 let &Bounds(l1, u1) = self; |
| |
242 debug_assert!(l1 <= u1 && l2 <= u2); |
| |
243 let l = l1.max(l2); |
| |
244 let u = u1.min(u2); |
| |
245 debug_assert!(l <= u); |
| |
246 if l < u { |
| |
247 Some(Bounds(l, u)) |
| |
248 } else { |
| |
249 None |
| |
250 } |
| |
251 } |
| |
252 } |