src/kernels/ball_indicator.rs

branch
dev
changeset 61
4f468d35fa29
parent 35
b087e3eab191
equal deleted inserted replaced
60:9738b51d90d7 61:4f468d35fa29
1
2 //! Implementation of the indicator function of a ball with respect to various norms. 1 //! Implementation of the indicator function of a ball with respect to various norms.
2 use super::base::*;
3 use crate::types::*;
4 use alg_tools::bisection_tree::{Bounds, Constant, GlobalAnalysis, LocalAnalysis, Support};
5 use alg_tools::coefficients::factorial;
6 use alg_tools::euclidean::StaticEuclidean;
7 use alg_tools::instance::Instance;
8 use alg_tools::loc::Loc;
9 use alg_tools::mapping::{DifferentiableImpl, Differential, LipschitzDifferentiableImpl, Mapping};
10 use alg_tools::maputil::array_init;
11 use alg_tools::norms::*;
12 use alg_tools::sets::Cube;
13 use anyhow::anyhow;
3 use float_extras::f64::tgamma as gamma; 14 use float_extras::f64::tgamma as gamma;
4 use numeric_literals::replace_float_literals; 15 use numeric_literals::replace_float_literals;
5 use serde::Serialize; 16 use serde::Serialize;
6 use alg_tools::types::*;
7 use alg_tools::norms::*;
8 use alg_tools::loc::Loc;
9 use alg_tools::sets::Cube;
10 use alg_tools::bisection_tree::{
11 Support,
12 Constant,
13 Bounds,
14 LocalAnalysis,
15 GlobalAnalysis,
16 };
17 use alg_tools::mapping::{
18 Mapping,
19 Differential,
20 DifferentiableImpl,
21 };
22 use alg_tools::instance::Instance;
23 use alg_tools::euclidean::StaticEuclidean;
24 use alg_tools::maputil::array_init;
25 use alg_tools::coefficients::factorial;
26 use crate::types::*;
27 use super::base::*;
28 17
29 /// Representation of the indicator of the ball $𝔹_q = \\{ x ∈ ℝ^N \mid \\|x\\|\_q ≤ r \\}$, 18 /// Representation of the indicator of the ball $𝔹_q = \\{ x ∈ ℝ^N \mid \\|x\\|\_q ≤ r \\}$,
30 /// where $q$ is the `Exponent`, and $r$ is the radius [`Constant`] `C`. 19 /// where $q$ is the `Exponent`, and $r$ is the radius [`Constant`] `C`.
31 #[derive(Copy,Clone,Serialize,Debug,Eq,PartialEq)] 20 #[derive(Copy, Clone, Serialize, Debug, Eq, PartialEq)]
32 pub struct BallIndicator<C : Constant, Exponent : NormExponent, const N : usize> { 21 pub struct BallIndicator<C: Constant, Exponent: NormExponent, const N: usize> {
33 /// The radius of the ball. 22 /// The radius of the ball.
34 pub r : C, 23 pub r: C,
35 /// The exponent $q$ of the norm creating the ball 24 /// The exponent $q$ of the norm creating the ball
36 pub exponent : Exponent, 25 pub exponent: Exponent,
37 } 26 }
38 27
39 /// Alias for the representation of the indicator of the $∞$-norm-ball 28 /// Alias for the representation of the indicator of the $∞$-norm-ball
40 /// $𝔹_∞ = \\{ x ∈ ℝ^N \mid \\|x\\|\_∞ ≤ c \\}$. 29 /// $𝔹_∞ = \\{ x ∈ ℝ^N \mid \\|x\\|\_∞ ≤ c \\}$.
41 pub type CubeIndicator<C, const N : usize> = BallIndicator<C, Linfinity, N>; 30 pub type CubeIndicator<C, const N: usize> = BallIndicator<C, Linfinity, N>;
42 31
43 #[replace_float_literals(C::Type::cast_from(literal))] 32 #[replace_float_literals(C::Type::cast_from(literal))]
44 impl<'a, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 33 impl<'a, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize>
45 Mapping<Loc<C::Type, N>> 34 Mapping<Loc<N, C::Type>> for BallIndicator<C, Exponent, N>
46 for BallIndicator<C, Exponent, N> 35 where
47 where 36 Loc<N, F>: Norm<Exponent, F>,
48 Loc<F, N> : Norm<F, Exponent>
49 { 37 {
50 type Codomain = C::Type; 38 type Codomain = C::Type;
51 39
52 #[inline] 40 #[inline]
53 fn apply<I : Instance<Loc<C::Type, N>>>(&self, x : I) -> Self::Codomain { 41 fn apply<I: Instance<Loc<N, C::Type>>>(&self, x: I) -> Self::Codomain {
54 let r = self.r.value(); 42 let r = self.r.value();
55 let n = x.eval(|x| x.norm(self.exponent)); 43 let n = x.eval(|x| x.norm(self.exponent));
56 if n <= r { 44 if n <= r {
57 1.0 45 1.0
58 } else { 46 } else {
59 0.0 47 0.0
60 } 48 }
61 } 49 }
62 } 50 }
63 51
64 impl<'a, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 52 impl<'a, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize>
65 DifferentiableImpl<Loc<C::Type, N>> 53 DifferentiableImpl<Loc<N, C::Type>> for BallIndicator<C, Exponent, N>
66 for BallIndicator<C, Exponent, N> 54 where
67 where 55 C: Constant,
68 C : Constant, 56 Loc<N, F>: Norm<Exponent, F>,
69 Loc<F, N> : Norm<F, Exponent> 57 {
70 { 58 type Derivative = Loc<N, C::Type>;
71 type Derivative = Loc<C::Type, N>; 59
72 60 #[inline]
73 #[inline] 61 fn differential_impl<I: Instance<Loc<N, C::Type>>>(&self, _x: I) -> Self::Derivative {
74 fn differential_impl<I : Instance<Loc<C::Type, N>>>(&self, _x : I) -> Self::Derivative {
75 Self::Derivative::origin() 62 Self::Derivative::origin()
76 } 63 }
77 } 64 }
78 65
79 impl<F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 66 impl<F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize> Lipschitz<L2>
80 Lipschitz<L2> 67 for BallIndicator<C, Exponent, N>
81 for BallIndicator<C, Exponent, N> 68 where
82 where C : Constant, 69 C: Constant,
83 Loc<F, N> : Norm<F, Exponent> { 70 Loc<N, F>: Norm<Exponent, F>,
71 {
84 type FloatType = C::Type; 72 type FloatType = C::Type;
85 73
86 fn lipschitz_factor(&self, _l2 : L2) -> Option<C::Type> { 74 fn lipschitz_factor(&self, _l2: L2) -> DynResult<C::Type> {
87 None 75 Err(anyhow!("Not a Lipschitz function"))
88 } 76 }
89 } 77 }
90 78
91 impl<'b, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 79 impl<'b, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize>
92 Lipschitz<L2> 80 LipschitzDifferentiableImpl<Loc<N, F>, L2> for BallIndicator<C, Exponent, N>
93 for Differential<'b, Loc<F, N>, BallIndicator<C, Exponent, N>> 81 where
94 where C : Constant, 82 C: Constant,
95 Loc<F, N> : Norm<F, Exponent> { 83 Loc<N, F>: Norm<Exponent, F>,
84 {
96 type FloatType = C::Type; 85 type FloatType = C::Type;
97 86
98 fn lipschitz_factor(&self, _l2 : L2) -> Option<C::Type> { 87 fn diff_lipschitz_factor(&self, _l2: L2) -> DynResult<C::Type> {
99 None 88 Err(anyhow!("Not a Lipschitz-differentiable function"))
100 } 89 }
101 } 90 }
102 91
103 impl<'a, 'b, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 92 impl<'b, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize> NormBounded<L2>
104 Lipschitz<L2> 93 for Differential<'b, Loc<N, F>, BallIndicator<C, Exponent, N>>
105 for Differential<'b, Loc<F, N>, &'a BallIndicator<C, Exponent, N>> 94 where
106 where C : Constant, 95 C: Constant,
107 Loc<F, N> : Norm<F, Exponent> { 96 Loc<N, F>: Norm<Exponent, F>,
97 {
108 type FloatType = C::Type; 98 type FloatType = C::Type;
109 99
110 fn lipschitz_factor(&self, _l2 : L2) -> Option<C::Type> { 100 fn norm_bound(&self, _l2: L2) -> C::Type {
111 None 101 F::INFINITY
112 } 102 }
113 } 103 }
114 104
115 105 impl<'a, 'b, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize>
116 impl<'b, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 106 NormBounded<L2> for Differential<'b, Loc<N, F>, &'a BallIndicator<C, Exponent, N>>
117 NormBounded<L2> 107 where
118 for Differential<'b, Loc<F, N>, BallIndicator<C, Exponent, N>> 108 C: Constant,
119 where C : Constant, 109 Loc<N, F>: Norm<Exponent, F>,
120 Loc<F, N> : Norm<F, Exponent> { 110 {
121 type FloatType = C::Type; 111 type FloatType = C::Type;
122 112
123 fn norm_bound(&self, _l2 : L2) -> C::Type { 113 fn norm_bound(&self, _l2: L2) -> C::Type {
124 F::INFINITY 114 F::INFINITY
125 } 115 }
126 } 116 }
127 117
128 impl<'a, 'b, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 118 impl<'a, F: Float, C: Constant<Type = F>, Exponent, const N: usize> Support<N, C::Type>
129 NormBounded<L2> 119 for BallIndicator<C, Exponent, N>
130 for Differential<'b, Loc<F, N>, &'a BallIndicator<C, Exponent, N>> 120 where
131 where C : Constant, 121 Exponent: NormExponent + Sync + Send + 'static,
132 Loc<F, N> : Norm<F, Exponent> { 122 Loc<N, F>: Norm<Exponent, F>,
133 type FloatType = C::Type; 123 Linfinity: Dominated<F, Exponent, Loc<N, F>>,
134 124 {
135 fn norm_bound(&self, _l2 : L2) -> C::Type { 125 #[inline]
136 F::INFINITY 126 fn support_hint(&self) -> Cube<N, F> {
137 }
138 }
139
140
141 impl<'a, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize>
142 Support<C::Type, N>
143 for BallIndicator<C, Exponent, N>
144 where Loc<F, N> : Norm<F, Exponent>,
145 Linfinity : Dominated<F, Exponent, Loc<F, N>> {
146
147 #[inline]
148 fn support_hint(&self) -> Cube<F,N> {
149 let r = Linfinity.from_norm(self.r.value(), self.exponent); 127 let r = Linfinity.from_norm(self.r.value(), self.exponent);
150 array_init(|| [-r, r]).into() 128 array_init(|| [-r, r]).into()
151 } 129 }
152 130
153 #[inline] 131 #[inline]
154 fn in_support(&self, x : &Loc<F,N>) -> bool { 132 fn in_support(&self, x: &Loc<N, F>) -> bool {
155 let r = Linfinity.from_norm(self.r.value(), self.exponent); 133 let r = Linfinity.from_norm(self.r.value(), self.exponent);
156 x.norm(self.exponent) <= r 134 x.norm(self.exponent) <= r
157 } 135 }
158 136
159 /// This can only really work in a reasonable fashion for N=1. 137 /// This can only really work in a reasonable fashion for N=1.
160 #[inline] 138 #[inline]
161 fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] { 139 fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
162 let r = Linfinity.from_norm(self.r.value(), self.exponent); 140 let r = Linfinity.from_norm(self.r.value(), self.exponent);
163 cube.map(|a, b| symmetric_interval_hint(r, a, b)) 141 cube.map(|a, b| symmetric_interval_hint(r, a, b))
164 } 142 }
165 } 143 }
166 144
167 #[replace_float_literals(F::cast_from(literal))] 145 #[replace_float_literals(F::cast_from(literal))]
168 impl<'a, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 146 impl<'a, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize>
169 GlobalAnalysis<F, Bounds<F>> 147 GlobalAnalysis<F, Bounds<F>> for BallIndicator<C, Exponent, N>
170 for BallIndicator<C, Exponent, N> 148 where
171 where Loc<F, N> : Norm<F, Exponent> { 149 Loc<N, F>: Norm<Exponent, F>,
150 {
172 #[inline] 151 #[inline]
173 fn global_analysis(&self) -> Bounds<F> { 152 fn global_analysis(&self) -> Bounds<F> {
174 Bounds(0.0, 1.0) 153 Bounds(0.0, 1.0)
175 } 154 }
176 } 155 }
177 156
178 #[replace_float_literals(F::cast_from(literal))] 157 #[replace_float_literals(F::cast_from(literal))]
179 impl<'a, F : Float, C : Constant<Type=F>, Exponent : NormExponent, const N : usize> 158 impl<'a, F: Float, C: Constant<Type = F>, Exponent: NormExponent, const N: usize> Norm<Linfinity, F>
180 Norm<F, Linfinity> 159 for BallIndicator<C, Exponent, N>
181 for BallIndicator<C, Exponent, N> 160 where
182 where Loc<F, N> : Norm<F, Exponent> { 161 Loc<N, F>: Norm<Exponent, F>,
183 #[inline] 162 {
184 fn norm(&self, _ : Linfinity) -> F { 163 #[inline]
164 fn norm(&self, _: Linfinity) -> F {
185 1.0 165 1.0
186 } 166 }
187 } 167 }
188 168
189 #[replace_float_literals(F::cast_from(literal))] 169 #[replace_float_literals(F::cast_from(literal))]
190 impl<'a, F : Float, C : Constant<Type=F>, const N : usize> 170 impl<'a, F: Float, C: Constant<Type = F>, const N: usize> Norm<L1, F> for BallIndicator<C, L1, N> {
191 Norm<F, L1> 171 #[inline]
192 for BallIndicator<C, L1, N> { 172 fn norm(&self, _: L1) -> F {
193 #[inline]
194 fn norm(&self, _ : L1) -> F {
195 // Using https://en.wikipedia.org/wiki/Volume_of_an_n-ball#Balls_in_Lp_norms, 173 // Using https://en.wikipedia.org/wiki/Volume_of_an_n-ball#Balls_in_Lp_norms,
196 // we have V_N^1(r) = (2r)^N / N! 174 // we have V_N^1(r) = (2r)^N / N!
197 let r = self.r.value(); 175 let r = self.r.value();
198 if N==1 { 176 if N == 1 {
199 2.0 * r 177 2.0 * r
200 } else if N==2 { 178 } else if N == 2 {
201 r*r 179 r * r
202 } else { 180 } else {
203 (2.0 * r).powi(N as i32) * F::cast_from(factorial(N)) 181 (2.0 * r).powi(N as i32) * F::cast_from(factorial(N))
204 } 182 }
205 } 183 }
206 } 184 }
207 185
208 #[replace_float_literals(F::cast_from(literal))] 186 #[replace_float_literals(F::cast_from(literal))]
209 impl<'a, F : Float, C : Constant<Type=F>, const N : usize> 187 impl<'a, F: Float, C: Constant<Type = F>, const N: usize> Norm<L1, F> for BallIndicator<C, L2, N> {
210 Norm<F, L1> 188 #[inline]
211 for BallIndicator<C, L2, N> { 189 fn norm(&self, _: L1) -> F {
212 #[inline]
213 fn norm(&self, _ : L1) -> F {
214 // See https://en.wikipedia.org/wiki/Volume_of_an_n-ball#The_volume. 190 // See https://en.wikipedia.org/wiki/Volume_of_an_n-ball#The_volume.
215 let r = self.r.value(); 191 let r = self.r.value();
216 let π = F::PI; 192 let π = F::PI;
217 if N==1 { 193 if N == 1 {
218 2.0 * r 194 2.0 * r
219 } else if N==2 { 195 } else if N == 2 {
220 π * (r * r) 196 π * (r * r)
221 } else { 197 } else {
222 let ndiv2 = F::cast_from(N) / 2.0; 198 let ndiv2 = F::cast_from(N) / 2.0;
223 let γ = F::cast_from(gamma((ndiv2 + 1.0).as_())); 199 let γ = F::cast_from(gamma((ndiv2 + 1.0).as_()));
224 π.powf(ndiv2) / γ * r.powi(N as i32) 200 π.powf(ndiv2) / γ * r.powi(N as i32)
225 } 201 }
226 } 202 }
227 } 203 }
228 204
229 #[replace_float_literals(F::cast_from(literal))] 205 #[replace_float_literals(F::cast_from(literal))]
230 impl<'a, F : Float, C : Constant<Type=F>, const N : usize> 206 impl<'a, F: Float, C: Constant<Type = F>, const N: usize> Norm<L1, F>
231 Norm<F, L1> 207 for BallIndicator<C, Linfinity, N>
232 for BallIndicator<C, Linfinity, N> { 208 {
233 #[inline] 209 #[inline]
234 fn norm(&self, _ : L1) -> F { 210 fn norm(&self, _: L1) -> F {
235 let two_r = 2.0 * self.r.value(); 211 let two_r = 2.0 * self.r.value();
236 two_r.powi(N as i32) 212 two_r.powi(N as i32)
237 } 213 }
238 } 214 }
239 215
240
241 macro_rules! indicator_local_analysis { 216 macro_rules! indicator_local_analysis {
242 ($exponent:ident) => { 217 ($exponent:ident) => {
243 impl<'a, F : Float, C : Constant<Type=F>, const N : usize> 218 impl<'a, F: Float, C: Constant<Type = F>, const N: usize> LocalAnalysis<F, Bounds<F>, N>
244 LocalAnalysis<F, Bounds<F>, N> 219 for BallIndicator<C, $exponent, N>
245 for BallIndicator<C, $exponent, N> 220 where
246 where Loc<F, N> : Norm<F, $exponent>, 221 Loc<N, F>: Norm<$exponent, F>,
247 Linfinity : Dominated<F, $exponent, Loc<F, N>> { 222 Linfinity: Dominated<F, $exponent, Loc<N, F>>,
223 {
248 #[inline] 224 #[inline]
249 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { 225 fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> {
250 // The function is maximised/minimised where the 2-norm is minimised/maximised. 226 // The function is maximised/minimised where the 2-norm is minimised/maximised.
251 let lower = self.apply(cube.maxnorm_point()); 227 let lower = self.apply(cube.maxnorm_point());
252 let upper = self.apply(cube.minnorm_point()); 228 let upper = self.apply(cube.minnorm_point());
253 Bounds(lower, upper) 229 Bounds(lower, upper)
254 } 230 }
255 } 231 }
256 } 232 };
257 } 233 }
258 234
259 indicator_local_analysis!(L1); 235 indicator_local_analysis!(L1);
260 indicator_local_analysis!(L2); 236 indicator_local_analysis!(L2);
261 indicator_local_analysis!(Linfinity); 237 indicator_local_analysis!(Linfinity);
262 238
263 239 #[replace_float_literals(F::cast_from(literal))]
264 #[replace_float_literals(F::cast_from(literal))] 240 impl<'a, F: Float, R, const N: usize> Mapping<Loc<N, F>> for AutoConvolution<CubeIndicator<R, N>>
265 impl<'a, F : Float, R, const N : usize> Mapping<Loc<F, N>> 241 where
266 for AutoConvolution<CubeIndicator<R, N>> 242 R: Constant<Type = F>,
267 where R : Constant<Type=F> { 243 {
268 type Codomain = F; 244 type Codomain = F;
269 245
270 #[inline] 246 #[inline]
271 fn apply<I : Instance<Loc<F, N>>>(&self, y : I) -> F { 247 fn apply<I: Instance<Loc<N, F>>>(&self, y: I) -> F {
272 let two_r = 2.0 * self.0.r.value(); 248 let two_r = 2.0 * self.0.r.value();
273 // This is just a product of one-dimensional versions 249 // This is just a product of one-dimensional versions
274 y.cow().iter().map(|&x| { 250 y.decompose()
275 0.0.max(two_r - x.abs()) 251 .iter()
276 }).product() 252 .map(|&x| 0.0.max(two_r - x.abs()))
277 } 253 .product()
278 } 254 }
279 255 }
280 #[replace_float_literals(F::cast_from(literal))] 256
281 impl<F : Float, R, const N : usize> Support<F, N> 257 #[replace_float_literals(F::cast_from(literal))]
282 for AutoConvolution<CubeIndicator<R, N>> 258 impl<F: Float, R, const N: usize> Support<N, F> for AutoConvolution<CubeIndicator<R, N>>
283 where R : Constant<Type=F> { 259 where
284 #[inline] 260 R: Constant<Type = F>,
285 fn support_hint(&self) -> Cube<F, N> { 261 {
262 #[inline]
263 fn support_hint(&self) -> Cube<N, F> {
286 let two_r = 2.0 * self.0.r.value(); 264 let two_r = 2.0 * self.0.r.value();
287 array_init(|| [-two_r, two_r]).into() 265 array_init(|| [-two_r, two_r]).into()
288 } 266 }
289 267
290 #[inline] 268 #[inline]
291 fn in_support(&self, y : &Loc<F, N>) -> bool { 269 fn in_support(&self, y: &Loc<N, F>) -> bool {
292 let two_r = 2.0 * self.0.r.value(); 270 let two_r = 2.0 * self.0.r.value();
293 y.iter().all(|x| x.abs() <= two_r) 271 y.iter().all(|x| x.abs() <= two_r)
294 } 272 }
295 273
296 #[inline] 274 #[inline]
297 fn bisection_hint(&self, cube : &Cube<F, N>) -> [Option<F>; N] { 275 fn bisection_hint(&self, cube: &Cube<N, F>) -> [Option<F>; N] {
298 let two_r = 2.0 * self.0.r.value(); 276 let two_r = 2.0 * self.0.r.value();
299 cube.map(|c, d| symmetric_interval_hint(two_r, c, d)) 277 cube.map(|c, d| symmetric_interval_hint(two_r, c, d))
300 } 278 }
301 } 279 }
302 280
303 #[replace_float_literals(F::cast_from(literal))] 281 #[replace_float_literals(F::cast_from(literal))]
304 impl<F : Float, R, const N : usize> GlobalAnalysis<F, Bounds<F>> 282 impl<F: Float, R, const N: usize> GlobalAnalysis<F, Bounds<F>>
305 for AutoConvolution<CubeIndicator<R, N>> 283 for AutoConvolution<CubeIndicator<R, N>>
306 where R : Constant<Type=F> { 284 where
285 R: Constant<Type = F>,
286 {
307 #[inline] 287 #[inline]
308 fn global_analysis(&self) -> Bounds<F> { 288 fn global_analysis(&self) -> Bounds<F> {
309 Bounds(0.0, self.apply(Loc::ORIGIN)) 289 Bounds(0.0, self.apply(Loc::ORIGIN))
310 } 290 }
311 } 291 }
312 292
313 impl<F : Float, R, const N : usize> LocalAnalysis<F, Bounds<F>, N> 293 impl<F: Float, R, const N: usize> LocalAnalysis<F, Bounds<F>, N>
314 for AutoConvolution<CubeIndicator<R, N>> 294 for AutoConvolution<CubeIndicator<R, N>>
315 where R : Constant<Type=F> { 295 where
316 #[inline] 296 R: Constant<Type = F>,
317 fn local_analysis(&self, cube : &Cube<F, N>) -> Bounds<F> { 297 {
298 #[inline]
299 fn local_analysis(&self, cube: &Cube<N, F>) -> Bounds<F> {
318 // The function is maximised/minimised where the absolute value is minimised/maximised. 300 // The function is maximised/minimised where the absolute value is minimised/maximised.
319 let lower = self.apply(cube.maxnorm_point()); 301 let lower = self.apply(cube.maxnorm_point());
320 let upper = self.apply(cube.minnorm_point()); 302 let upper = self.apply(cube.minnorm_point());
321 Bounds(lower, upper) 303 Bounds(lower, upper)
322 } 304 }

mercurial