1 // Copright (C) 1999-2006, Bernd Gaertner
2 // $Revision: 1.3 $
3 // $Date: 2006/11/16 08:01:52 $
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA,
18 // or download the License terms from prep.ai.mit.edu/pub/gnu/COPYING-2.0.
19 //
20 // Contact:
21 // --------
22 // Bernd Gaertner
23 // Institute of Theoretical Computer Science
24 // ETH Zuerich
25 // CAB G32.2
26 // CH-8092 Zuerich, Switzerland
27 // http://www.inf.ethz.ch/personal/gaertner
28
29 #include <cstdlib>
30 #include <cassert>
31 #include <cmath>
32 #include <iostream>
33 #include <list>
34
35 // Functions
36 // =========
mb_sqr(double r)37 inline double mb_sqr (double r) {return r*r;}
38
39 // Class Declarations
40 // ==================
41
42 // smallest enclosing ball of a set of n points in dimension d
43 template <int d>
44 class Miniball;
45
46 // smallest ball with a set of n <= d+1 points *on the boundary*
47 template <int d>
48 class Miniball_b;
49
50 // point in dimension d
51 template <int d>
52 class Point;
53
54
55 // Class Definitions
56 // =================
57
58 // Miniball
59 // --------
60
61 template <int d>
62 class Miniball {
63 public:
64 // types
65 typedef typename std::list<Point<d> >::iterator It;
66 typedef typename std::list<Point<d> >::const_iterator Cit;
67
68 private:
69 // data members
70 std::list<Point<d> > L; // internal point set
71 Miniball_b<d> B; // the current ball
72 It support_end; // past-the-end iterator of support set
73
74 // private methods
75 void mtf_mb (It k);
76 void pivot_mb (It k);
77 void move_to_front (It j);
78 double max_excess (It t, It i, It& pivot) const;
79
80 public:
81 // creates an empty ball
Miniball()82 Miniball() {}
83
84 // copies p to the internal point set
85 void check_in (const Point<d>& p);
86
87 // builds the smallest enclosing ball of the internal point set
88 void build ();
89
90 // returns center of the ball (undefined if ball is empty)
91 Point<d> center() const;
92
93 // returns squared_radius of the ball (-1 if ball is empty)
94 double squared_radius () const;
95
96 // returns size of internal point set
97 int nr_points () const;
98
99 // returns begin- and past-the-end iterators for internal point set
100 Cit points_begin () const;
101 Cit points_end () const;
102
103 // returns size of support point set; this set has the following properties:
104 // - there are at most d+1 support points,
105 // - all support points are on the boundary of the computed ball, and
106 // - the smallest enclosing ball of the support point set equals the
107 // smallest enclosing ball of the internal point set
108 int nr_support_points () const;
109
110 // returns begin- and past-the-end iterators for internal point set
111 Cit support_points_begin () const;
112 Cit support_points_end () const;
113
114 // assesses the quality of the computed ball. The return value is the
115 // maximum squared distance of any support point or point outside the
116 // ball to the boundary of the ball, divided by the squared radius of
117 // the ball. If everything went fine, this will be less than e-15 and
118 // says that the computed ball approximately contains all the internal
119 // points and has all the support points on the boundary.
120 //
121 // The slack parameter that is set by the method says something about
122 // whether the computed ball is really the *smallest* enclosing ball
123 // of the support points; if everything went fine, this value will be 0;
124 // a positive value may indicate that the ball is not smallest possible,
125 // with the deviation from optimality growing with the slack
126 double accuracy (double& slack) const;
127
128 // returns true if the accuracy is below the given tolerance and the
129 // slack is 0
130 bool is_valid (double tolerance = 1e-15) const;
131 };
132
133
134 // Miniball_b
135 // ----------
136
137 template <int d>
138 class Miniball_b {
139 private:
140 // data members
141 int m, s; // size and number of support points
142 double q0[d];
143
144 double z[d+1];
145 double f[d+1];
146 double v[d+1][d];
147 double a[d+1][d];
148
149 double c[d+1][d];
150 double sqr_r[d+1];
151
152 double* current_c; // refers to some c[j]
153 double current_sqr_r;
154
155 public:
Miniball_b()156 Miniball_b() {reset();}
157
158 // access
159 const double* center() const;
160 double squared_radius() const;
161 int size() const;
162 int support_size() const;
163 double excess (const Point<d>& p) const;
164
165 // modification
166 void reset(); // generates empty sphere with m=s=0
167 bool push (const Point<d>& p);
168 void pop ();
169
170 // checking
171 double slack() const;
172 };
173
174 // Point (inline)
175 // -------------
176 template <int d>
177 class Point {
178 private:
179 double coord [d];
180
181 public:
182 // default
Point()183 Point()
184 {}
185
186 // copy from Point
Point(const Point & p)187 Point (const Point& p)
188 {
189 for (int i=0; i<d; ++i)
190 coord[i] = p.coord[i];
191 }
192
193 // copy from double*
Point(const double * p)194 Point (const double* p)
195 {
196 for (int i=0; i<d; ++i)
197 coord[i] = p[i];
198 }
199
200 // assignment
201 Point& operator = (const Point& p)
202 {
203 if (this != &p)
204 for (int i=0; i<d; ++i)
205 coord[i] = p.coord[i];
206 return *this;
207 }
208
209 // coordinate access
210 double& operator [] (int i)
211 {
212 return coord[i];
213 }
214 const double& operator [] (int i) const
215 {
216 return coord[i];
217 }
begin()218 const double* begin() const
219 {
220 return coord;
221 }
end()222 const double* end() const
223 {
224 return coord+d;
225 }
226 };
227
228
229 // Class Implementations
230 // =====================
231
232 // Miniball
233 // --------
234
235 template <int d>
check_in(const Point<d> & p)236 void Miniball<d>::check_in (const Point<d>& p)
237 {
238 L.push_back(p);
239 }
240
241
242 template <int d>
build()243 void Miniball<d>::build ()
244 {
245 B.reset();
246 support_end = L.begin();
247 pivot_mb (L.end());
248 }
249
250
251 template <int d>
mtf_mb(It i)252 void Miniball<d>::mtf_mb (It i)
253 {
254 support_end = L.begin();
255 if ((B.size())==d+1) return;
256 for (It k=L.begin(); k!=i;) {
257 It j=k++;
258 if (B.excess(*j) > 0) {
259 if (B.push(*j)) {
260 mtf_mb (j);
261 B.pop();
262 move_to_front(j);
263 }
264 }
265 }
266 }
267
268 template <int d>
move_to_front(It j)269 void Miniball<d>::move_to_front (It j)
270 {
271 if (support_end == j)
272 support_end++;
273 L.splice (L.begin(), L, j);
274 }
275
276
277 template <int d>
pivot_mb(It i)278 void Miniball<d>::pivot_mb (It i)
279 {
280 It t = ++L.begin();
281 mtf_mb (t);
282 double max_e, old_sqr_r = -1;
283 do {
284 It pivot;
285 max_e = max_excess (t, i, pivot);
286 if (max_e > 0) {
287 t = support_end;
288 if (t==pivot) ++t;
289 old_sqr_r = B.squared_radius();
290 B.push (*pivot);
291 mtf_mb (support_end);
292 B.pop();
293 move_to_front (pivot);
294 }
295 } while ((max_e > 0) && (B.squared_radius() > old_sqr_r));
296 }
297
298
299 template <int d>
max_excess(It t,It i,It & pivot)300 double Miniball<d>::max_excess (It t, It i, It& pivot) const
301 {
302 const double *c = B.center(), sqr_r = B.squared_radius();
303 double e, max_e = 0;
304 for (It k=t; k!=i; ++k) {
305 const double *p = (*k).begin();
306 e = -sqr_r;
307 for (int j=0; j<d; ++j)
308 e += mb_sqr(p[j]-c[j]);
309 if (e > max_e) {
310 max_e = e;
311 pivot = k;
312 }
313 }
314 return max_e;
315 }
316
317
318
319 template <int d>
center()320 Point<d> Miniball<d>::center () const
321 {
322 return Point<d>(B.center());
323 }
324
325 template <int d>
squared_radius()326 double Miniball<d>::squared_radius () const
327 {
328 return B.squared_radius();
329 }
330
331
332 template <int d>
nr_points()333 int Miniball<d>::nr_points () const
334 {
335 return L.size();
336 }
337
338 template <int d>
points_begin()339 typename Miniball<d>::Cit Miniball<d>::points_begin () const
340 {
341 return L.begin();
342 }
343
344 template <int d>
points_end()345 typename Miniball<d>::Cit Miniball<d>::points_end () const
346 {
347 return L.end();
348 }
349
350
351 template <int d>
nr_support_points()352 int Miniball<d>::nr_support_points () const
353 {
354 return B.support_size();
355 }
356
357 template <int d>
support_points_begin()358 typename Miniball<d>::Cit Miniball<d>::support_points_begin () const
359 {
360 return L.begin();
361 }
362
363 template <int d>
support_points_end()364 typename Miniball<d>::Cit Miniball<d>::support_points_end () const
365 {
366 return support_end;
367 }
368
369
370
371 template <int d>
accuracy(double & slack)372 double Miniball<d>::accuracy (double& slack) const
373 {
374 double e, max_e = 0;
375 int n_supp=0;
376 Cit i;
377 for (i=L.begin(); i!=support_end; ++i,++n_supp)
378 if ((e = std::abs (B.excess (*i))) > max_e)
379 max_e = e;
380
381 // you've found a non-numerical problem if the following ever fails
382 assert (n_supp == nr_support_points());
383
384 for (i=support_end; i!=L.end(); ++i)
385 if ((e = B.excess (*i)) > max_e)
386 max_e = e;
387
388 slack = B.slack();
389 return (max_e/squared_radius());
390 }
391
392
393 template <int d>
is_valid(double tolerance)394 bool Miniball<d>::is_valid (double tolerance) const
395 {
396 double slack;
397 return ( (accuracy (slack) < tolerance) && (slack == 0) );
398 }
399
400
401
402 // Miniball_b
403 // ----------
404
405 template <int d>
center()406 const double* Miniball_b<d>::center () const
407 {
408 return current_c;
409 }
410
411 template <int d>
squared_radius()412 double Miniball_b<d>::squared_radius() const
413 {
414 return current_sqr_r;
415 }
416
417 template <int d>
size()418 int Miniball_b<d>::size() const
419 {
420 return m;
421 }
422
423 template <int d>
support_size()424 int Miniball_b<d>::support_size() const
425 {
426 return s;
427 }
428
429 template <int d>
excess(const Point<d> & p)430 double Miniball_b<d>::excess (const Point<d>& p) const
431 {
432 double e = -current_sqr_r;
433 for (int k=0; k<d; ++k)
434 e += mb_sqr(p[k]-current_c[k]);
435 return e;
436 }
437
438
439
440 template <int d>
reset()441 void Miniball_b<d>::reset ()
442 {
443 m = s = 0;
444 // we misuse c[0] for the center of the empty sphere
445 for (int j=0; j<d; ++j)
446 c[0][j]=0;
447 current_c = c[0];
448 current_sqr_r = -1;
449 }
450
451
452 template <int d>
pop()453 void Miniball_b<d>::pop ()
454 {
455 --m;
456 }
457
458
459 template <int d>
push(const Point<d> & p)460 bool Miniball_b<d>::push (const Point<d>& p)
461 {
462 int i, j;
463 double eps = 1e-32;
464 if (m==0) {
465 for (i=0; i<d; ++i)
466 q0[i] = p[i];
467 for (i=0; i<d; ++i)
468 c[0][i] = q0[i];
469 sqr_r[0] = 0;
470 } else {
471 // set v_m to Q_m
472 for (i=0; i<d; ++i)
473 v[m][i] = p[i]-q0[i];
474
475 // compute the a_{m,i}, i< m
476 for (i=1; i<m; ++i) {
477 a[m][i] = 0;
478 for (j=0; j<d; ++j)
479 a[m][i] += v[i][j] * v[m][j];
480 a[m][i]*=(2/z[i]);
481 }
482
483 // update v_m to Q_m-\bar{Q}_m
484 for (i=1; i<m; ++i) {
485 for (j=0; j<d; ++j)
486 v[m][j] -= a[m][i]*v[i][j];
487 }
488
489 // compute z_m
490 z[m]=0;
491 for (j=0; j<d; ++j)
492 z[m] += mb_sqr(v[m][j]);
493 z[m]*=2;
494
495 // reject push if z_m too small
496 if (z[m]<eps*current_sqr_r) {
497 return false;
498 }
499
500 // update c, sqr_r
501 double e = -sqr_r[m-1];
502 for (i=0; i<d; ++i)
503 e += mb_sqr(p[i]-c[m-1][i]);
504 f[m]=e/z[m];
505
506 for (i=0; i<d; ++i)
507 c[m][i] = c[m-1][i]+f[m]*v[m][i];
508 sqr_r[m] = sqr_r[m-1] + e*f[m]/2;
509 }
510 current_c = c[m];
511 current_sqr_r = sqr_r[m];
512 s = ++m;
513 return true;
514 }
515
516 template <int d>
slack()517 double Miniball_b<d>::slack () const
518 {
519 double l[d+1], min_l=0;
520 l[0] = 1;
521 for (int i=s-1; i>0; --i) {
522 l[i] = f[i];
523 for (int k=s-1; k>i; --k)
524 l[i]-=a[k][i]*l[k];
525 if (l[i] < min_l) min_l = l[i];
526 l[0] -= l[i];
527 }
528 if (l[0] < min_l) min_l = l[0];
529 return ( (min_l < 0) ? -min_l : 0);
530 }
531
532 // Point
533 // -----
534
535 // Output
536 template <int d>
537 std::ostream& operator << (std::ostream& os, const Point<d>& p)
538 {
539 os << "(";
540 for (int i=0; i<d-1; ++i)
541 os << p[i] << ", ";
542 os << p[d-1] << ")";
543 return os;
544 }
545