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