src/bounds.rs

changeset 198
3868555d135c
parent 197
1f301affeae3
equal deleted inserted replaced
94:1f19c6bbf07b 198:3868555d135c
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 }

mercurial