888 lines
27 KiB
C
888 lines
27 KiB
C
|
// Copyright (c) 1997-2001 ETH Zurich (Switzerland).
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// This file is part of CGAL (www.cgal.org).
|
||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||
|
// General Public License as published by the Free Software Foundation,
|
||
|
// either version 3 of the License, or (at your option) any later version.
|
||
|
//
|
||
|
// Licensees holding a valid commercial license may use this file in
|
||
|
// accordance with the commercial license agreement provided with the software.
|
||
|
//
|
||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
//
|
||
|
// $URL$
|
||
|
// $Id$
|
||
|
// SPDX-License-Identifier: GPL-3.0+
|
||
|
//
|
||
|
//
|
||
|
// Author(s) : Sven Schoenherr <sven@inf.ethz.ch>
|
||
|
|
||
|
#ifndef CGAL_MIN_ANNULUS_D_H
|
||
|
#define CGAL_MIN_ANNULUS_D_H
|
||
|
|
||
|
#include <CGAL/license/Bounding_volumes.h>
|
||
|
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
# pragma warning(push)
|
||
|
# pragma warning(disable: 4244) // conversion warning in Boost iterator_adaptor
|
||
|
#endif
|
||
|
|
||
|
// includes
|
||
|
// --------
|
||
|
#include <CGAL/Optimisation/basic.h>
|
||
|
#include <CGAL/function_objects.h>
|
||
|
#include <CGAL/QP_options.h>
|
||
|
#include <CGAL/QP_solver/QP_solver.h>
|
||
|
#include <CGAL/QP_solver/functors.h>
|
||
|
#include <CGAL/QP_solver/QP_full_filtered_pricing.h>
|
||
|
#include <CGAL/QP_solver/QP_full_exact_pricing.h>
|
||
|
#include <CGAL/boost/iterator/counting_iterator.hpp>
|
||
|
#include <CGAL/boost/iterator/transform_iterator.hpp>
|
||
|
#include <boost/functional.hpp>
|
||
|
|
||
|
// here is how it works. We have d+2 variables:
|
||
|
// R (big radius), r (small radius), c (center). The problem is
|
||
|
//
|
||
|
// min R^2 - r^2
|
||
|
// s.t. ||p - c||^2 >= r^2 for all p
|
||
|
// ||p - c||^2 <= R^2 for all p
|
||
|
//
|
||
|
// This looks nonlinear, but we can in fact make the substitutions
|
||
|
// u = R^2 - c^Tc, v = r^2 - c^Tc and get the following equivalent
|
||
|
// linear program:
|
||
|
//
|
||
|
// min u - v
|
||
|
// s.t. p^Tp - 2p^Tc >= v for all p
|
||
|
// p^Tp - 2p^Tc <= u for all p
|
||
|
//
|
||
|
// or
|
||
|
//
|
||
|
// max v - u
|
||
|
// s.t. v + 2p_1c_1 + 2p_2c_2 + ... + 2p_dc_d <= p^Tp for all p
|
||
|
// - u - 2p_1c_1 - 2p_2c_2 - ... - 2p_dc_d <= -p^Tp for all p
|
||
|
//
|
||
|
// When we introduce a dual variable x_p for every constraint in the first
|
||
|
// set and a dual variable y_p for every constraint in the second set,
|
||
|
// we obtain the following dual program:
|
||
|
//
|
||
|
// min \sum_p x_p p^Tp - \sum_p y_p p^Tp
|
||
|
// s.t.
|
||
|
// 2\sum_p x_p p - 2\sum_p y_p p = 0
|
||
|
// \sum_p x_p = 1 (constraint for v)
|
||
|
// - \sum_p y_p = -1 (constraint for u)
|
||
|
// x_p >= 0 for all p
|
||
|
// y_p >= 0 for all p
|
||
|
//
|
||
|
// in the following functors, the ordering of the constraints is as above;
|
||
|
// the indices of the variables are: x_p_j <-> 2 * j, y_p_j <-> 2 * j + 1
|
||
|
// we also make the substitutions x'_p = x_p / h_p^2, y'_p = y_p / h_p^2
|
||
|
// where h_p is the homogenizing coordinate of p, in order to allow
|
||
|
// homogeneous points. This, however, means that the computed annulus is
|
||
|
// not necessarily correct. If P is a set of homogeneous points,
|
||
|
// P = { (p_0,...,p_{d-1}, h_p) },
|
||
|
// then we always get a *feasible* annulus for the point set
|
||
|
// P' = { (p_0*h_p,...,p_{d-1}*h_p, h_p*h_p) }.
|
||
|
// If the type NT is inexact, this annulus might not even be optimal, since
|
||
|
// the objective function involves terms p^Tp that might not be exactly
|
||
|
// computed -> document all this!!!
|
||
|
|
||
|
namespace CGAL {
|
||
|
|
||
|
namespace MA_detail {
|
||
|
|
||
|
// functor for a fixed column of A
|
||
|
template <class NT, class Iterator>
|
||
|
class A_column : public CGAL::cpp98::unary_function <int, NT>
|
||
|
{
|
||
|
public:
|
||
|
typedef NT result_type;
|
||
|
A_column()
|
||
|
{}
|
||
|
|
||
|
A_column (int j, int d, Iterator it)
|
||
|
: j_ (j), d_ (d), it_ (it), h_p (*(it+d)), nt_0_ (0), nt_2_ (2)
|
||
|
{}
|
||
|
|
||
|
result_type operator() (int i) const
|
||
|
{
|
||
|
if (j_ % 2 == 0) {
|
||
|
// column for x_p
|
||
|
if (i < d_) return *(it_ + i) * h_p * nt_2_;
|
||
|
if (i == d_) return h_p * h_p;
|
||
|
return nt_0_;
|
||
|
} else {
|
||
|
// column for y_p
|
||
|
if (i < d_) return -(*(it_ + i)) * h_p * nt_2_;
|
||
|
if (i == d_+1) return -h_p * h_p;
|
||
|
return nt_0_;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int j_; // column number
|
||
|
int d_; // dimension
|
||
|
Iterator it_; // the iterator through the column's point
|
||
|
NT h_p; // the homogenizing coordinate of p
|
||
|
NT nt_0_;
|
||
|
NT nt_2_;
|
||
|
};
|
||
|
|
||
|
// functor for matrix A
|
||
|
template <class NT, class Access_coordinate_begin_d,
|
||
|
class Point_iterator >
|
||
|
class A_matrix : public CGAL::cpp98::unary_function
|
||
|
<int, boost::transform_iterator <A_column
|
||
|
<NT, typename Access_coordinate_begin_d::Coordinate_iterator>,
|
||
|
boost::counting_iterator<int> > >
|
||
|
{
|
||
|
typedef typename MA_detail::A_column
|
||
|
<NT, typename Access_coordinate_begin_d::Coordinate_iterator> A_column;
|
||
|
public:
|
||
|
typedef boost::transform_iterator
|
||
|
<A_column, boost::counting_iterator<int> > result_type;
|
||
|
|
||
|
A_matrix ()
|
||
|
{}
|
||
|
|
||
|
A_matrix (int d,
|
||
|
const Access_coordinate_begin_d& da_coord,
|
||
|
Point_iterator P)
|
||
|
: d_ (d), da_coord_ (da_coord), P_ (P)
|
||
|
{}
|
||
|
|
||
|
result_type operator () (int j) const
|
||
|
{
|
||
|
return result_type
|
||
|
(0, A_column (j, d_, da_coord_ (*(P_+j/2))));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int d_; // dimension
|
||
|
Access_coordinate_begin_d da_coord_; // data accessor
|
||
|
Point_iterator P_; // point set P
|
||
|
};
|
||
|
|
||
|
// The functor necessary to realize access to b
|
||
|
template <class NT>
|
||
|
class B_vector : public CGAL::cpp98::unary_function<int, NT>
|
||
|
{
|
||
|
public:
|
||
|
typedef NT result_type;
|
||
|
B_vector()
|
||
|
{}
|
||
|
|
||
|
B_vector (int d)
|
||
|
: d_ (d), nt_0_ (0), nt_1_ (1)
|
||
|
{}
|
||
|
|
||
|
result_type operator() (int i) const
|
||
|
{
|
||
|
if (i == d_) return nt_1_;
|
||
|
if (i == d_+1) return -nt_1_;
|
||
|
return nt_0_;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int d_;
|
||
|
NT nt_0_;
|
||
|
NT nt_1_;
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
// Class interfaces
|
||
|
// ================
|
||
|
template < class Traits_ >
|
||
|
class Min_annulus_d {
|
||
|
public:
|
||
|
// self
|
||
|
typedef Traits_ Traits;
|
||
|
typedef Min_annulus_d<Traits> Self;
|
||
|
|
||
|
// types from the traits class
|
||
|
typedef typename Traits::Point_d Point;
|
||
|
|
||
|
typedef typename Traits::Rep_tag Rep_tag;
|
||
|
|
||
|
typedef typename Traits::RT RT;
|
||
|
typedef typename Traits::FT FT;
|
||
|
|
||
|
typedef typename Traits::Access_dimension_d
|
||
|
Access_dimension_d;
|
||
|
typedef typename Traits::Access_coordinates_begin_d
|
||
|
Access_coordinates_begin_d;
|
||
|
|
||
|
typedef typename Traits::Construct_point_d
|
||
|
Construct_point_d;
|
||
|
|
||
|
typedef typename Traits::ET ET;
|
||
|
typedef typename Traits::NT NT;
|
||
|
|
||
|
// public types
|
||
|
typedef std::vector<Point> Point_vector;
|
||
|
typedef typename Point_vector::const_iterator
|
||
|
Point_iterator;
|
||
|
|
||
|
private:
|
||
|
// QP solver iterator types
|
||
|
typedef MA_detail::A_matrix <NT, Access_coordinates_begin_d,
|
||
|
Point_iterator> A_matrix;
|
||
|
typedef boost::transform_iterator
|
||
|
<A_matrix,
|
||
|
boost::counting_iterator<int> > A_iterator;
|
||
|
|
||
|
typedef MA_detail::B_vector <NT> B_vector;
|
||
|
typedef boost::transform_iterator
|
||
|
<B_vector,
|
||
|
boost::counting_iterator<int> > B_iterator;
|
||
|
|
||
|
typedef CGAL::Const_oneset_iterator<CGAL::Comparison_result> R_iterator;
|
||
|
|
||
|
typedef std::vector<NT> C_vector;
|
||
|
typedef typename C_vector::const_iterator C_iterator;
|
||
|
|
||
|
// Program type
|
||
|
typedef CGAL::Nonnegative_linear_program_from_iterators
|
||
|
<A_iterator, B_iterator, R_iterator, C_iterator> LP;
|
||
|
|
||
|
// Tags
|
||
|
typedef QP_solver_impl::QP_tags <Tag_true, Tag_true> QP_tags;
|
||
|
|
||
|
// Solver types
|
||
|
typedef CGAL::QP_solver <LP, ET, QP_tags > Solver;
|
||
|
|
||
|
typedef typename Solver::Pricing_strategy Pricing_strategy;
|
||
|
|
||
|
// types from the QP solver
|
||
|
typedef typename Solver::Basic_variable_index_iterator
|
||
|
Basic_variable_index_iterator;
|
||
|
|
||
|
// private types
|
||
|
typedef std::vector<ET> ET_vector;
|
||
|
|
||
|
typedef QP_access_by_index
|
||
|
<typename std::vector<Point>::const_iterator, int> Point_by_index;
|
||
|
|
||
|
typedef boost::binder2nd< std::divides<int> >
|
||
|
Divide;
|
||
|
|
||
|
typedef std::vector<int> Index_vector;
|
||
|
|
||
|
typedef std::vector<NT> NT_vector;
|
||
|
typedef std::vector<NT_vector> NT_matrix;
|
||
|
|
||
|
|
||
|
public:
|
||
|
// public types
|
||
|
|
||
|
typedef CGAL::Join_input_iterator_1<
|
||
|
Basic_variable_index_iterator,
|
||
|
CGAL::Unary_compose_1<Point_by_index,Divide> >
|
||
|
Support_point_iterator;
|
||
|
|
||
|
|
||
|
typedef typename Index_vector::const_iterator IVCI;
|
||
|
typedef CGAL::Join_input_iterator_1<
|
||
|
IVCI, Point_by_index >
|
||
|
Inner_support_point_iterator;
|
||
|
typedef CGAL::Join_input_iterator_1<
|
||
|
IVCI, Point_by_index >
|
||
|
Outer_support_point_iterator;
|
||
|
|
||
|
typedef IVCI Inner_support_point_index_iterator;
|
||
|
typedef IVCI Outer_support_point_index_iterator;
|
||
|
|
||
|
typedef typename ET_vector::const_iterator
|
||
|
Coordinate_iterator;
|
||
|
|
||
|
|
||
|
// creation
|
||
|
Min_annulus_d( const Traits& traits = Traits())
|
||
|
: tco( traits), da_coord(tco.access_coordinates_begin_d_object()),
|
||
|
d( -1), solver(0){}
|
||
|
|
||
|
template < class InputIterator >
|
||
|
Min_annulus_d( InputIterator first,
|
||
|
InputIterator last,
|
||
|
const Traits& traits = Traits())
|
||
|
: tco( traits), da_coord(tco.access_coordinates_begin_d_object()),
|
||
|
solver(0) {
|
||
|
set( first, last);
|
||
|
}
|
||
|
|
||
|
~Min_annulus_d() {
|
||
|
if (solver)
|
||
|
delete solver;
|
||
|
}
|
||
|
|
||
|
// access to point set
|
||
|
int ambient_dimension( ) const { return d; }
|
||
|
|
||
|
int number_of_points( ) const { return static_cast<int>(points.size()); }
|
||
|
|
||
|
Point_iterator points_begin( ) const { return points.begin(); }
|
||
|
Point_iterator points_end ( ) const { return points.end (); }
|
||
|
|
||
|
// access to support points
|
||
|
int
|
||
|
number_of_support_points( ) const
|
||
|
{ return number_of_points() < 2 ?
|
||
|
number_of_points() :
|
||
|
solver->number_of_basic_variables(); }
|
||
|
|
||
|
Support_point_iterator
|
||
|
support_points_begin() const {
|
||
|
CGAL_optimisation_assertion_msg(number_of_points() >= 2,
|
||
|
"support_points_begin: not enough points");
|
||
|
return Support_point_iterator(
|
||
|
solver->basic_original_variable_indices_begin(),
|
||
|
CGAL::compose1_1(
|
||
|
Point_by_index( points.begin()),
|
||
|
boost::bind2nd( std::divides<int>(), 2)));
|
||
|
}
|
||
|
|
||
|
Support_point_iterator
|
||
|
support_points_end() const {
|
||
|
CGAL_optimisation_assertion_msg(number_of_points() >= 2,
|
||
|
"support_points_begin: not enough points");
|
||
|
return Support_point_iterator(
|
||
|
solver->basic_original_variable_indices_end(),
|
||
|
CGAL::compose1_1(
|
||
|
Point_by_index( points.begin()),
|
||
|
boost::bind2nd( std::divides<int>(), 2)));
|
||
|
}
|
||
|
|
||
|
int number_of_inner_support_points() const { return static_cast<int>(inner_indices.size());}
|
||
|
int number_of_outer_support_points() const { return static_cast<int>(outer_indices.size());}
|
||
|
|
||
|
Inner_support_point_iterator
|
||
|
inner_support_points_begin() const
|
||
|
{ return Inner_support_point_iterator(
|
||
|
inner_indices.begin(),
|
||
|
Point_by_index( points.begin())); }
|
||
|
|
||
|
Inner_support_point_iterator
|
||
|
inner_support_points_end() const
|
||
|
{ return Inner_support_point_iterator(
|
||
|
inner_indices.end(),
|
||
|
Point_by_index( points.begin())); }
|
||
|
|
||
|
Outer_support_point_iterator
|
||
|
outer_support_points_begin() const
|
||
|
{ return Outer_support_point_iterator(
|
||
|
outer_indices.begin(),
|
||
|
Point_by_index( points.begin())); }
|
||
|
|
||
|
Outer_support_point_iterator
|
||
|
outer_support_points_end() const
|
||
|
{ return Outer_support_point_iterator(
|
||
|
outer_indices.end(),
|
||
|
Point_by_index( points.begin())); }
|
||
|
|
||
|
Inner_support_point_index_iterator
|
||
|
inner_support_points_indices_begin() const
|
||
|
{ return inner_indices.begin(); }
|
||
|
|
||
|
Inner_support_point_index_iterator
|
||
|
inner_support_points_indices_end() const
|
||
|
{ return inner_indices.end(); }
|
||
|
|
||
|
Outer_support_point_index_iterator
|
||
|
outer_support_points_indices_begin() const
|
||
|
{ return outer_indices.begin(); }
|
||
|
|
||
|
Outer_support_point_index_iterator
|
||
|
outer_support_points_indices_end() const
|
||
|
{ return outer_indices.end(); }
|
||
|
|
||
|
|
||
|
// access to center (rational representation)
|
||
|
Coordinate_iterator
|
||
|
center_coordinates_begin( ) const { return center_coords.begin(); }
|
||
|
|
||
|
Coordinate_iterator
|
||
|
center_coordinates_end ( ) const { return center_coords.end (); }
|
||
|
|
||
|
// access to squared radii (rational representation)
|
||
|
ET squared_inner_radius_numerator( ) const { return sqr_i_rad_numer; }
|
||
|
ET squared_outer_radius_numerator( ) const { return sqr_o_rad_numer; }
|
||
|
ET squared_radii_denominator ( ) const { return sqr_rad_denom; }
|
||
|
|
||
|
// access to center and squared radii
|
||
|
// NOTE: an implicit conversion from ET to RT must be available!
|
||
|
Point
|
||
|
center( ) const
|
||
|
{ CGAL_optimisation_precondition( ! is_empty());
|
||
|
return tco.construct_point_d_object()( ambient_dimension(),
|
||
|
center_coordinates_begin(),
|
||
|
center_coordinates_end()); }
|
||
|
|
||
|
FT
|
||
|
squared_inner_radius( ) const
|
||
|
{ CGAL_optimisation_precondition( ! is_empty());
|
||
|
return FT( squared_inner_radius_numerator()) /
|
||
|
FT( squared_radii_denominator()); }
|
||
|
|
||
|
FT
|
||
|
squared_outer_radius( ) const
|
||
|
{ CGAL_optimisation_precondition( ! is_empty());
|
||
|
return FT( squared_outer_radius_numerator()) /
|
||
|
FT( squared_radii_denominator()); }
|
||
|
|
||
|
// predicates
|
||
|
CGAL::Bounded_side
|
||
|
bounded_side( const Point& p) const
|
||
|
{ CGAL_optimisation_precondition(
|
||
|
is_empty() || tco.access_dimension_d_object()( p) == d);
|
||
|
ET sqr_d = sqr_dist( p);
|
||
|
ET h_p_sqr = da_coord(p)[d] * da_coord(p)[d];
|
||
|
return CGAL::Bounded_side
|
||
|
(CGAL_NTS sign( sqr_d - h_p_sqr * sqr_i_rad_numer)
|
||
|
* CGAL_NTS sign( h_p_sqr * sqr_o_rad_numer - sqr_d)); }
|
||
|
|
||
|
bool
|
||
|
has_on_bounded_side( const Point& p) const
|
||
|
{ CGAL_optimisation_precondition(
|
||
|
is_empty() || tco.access_dimension_d_object()( p) == d);
|
||
|
ET sqr_d = sqr_dist( p);
|
||
|
ET h_p_sqr = da_coord(p)[d] * da_coord(p)[d];
|
||
|
return ( ( h_p_sqr * sqr_i_rad_numer < sqr_d) &&
|
||
|
( sqr_d < h_p_sqr * sqr_o_rad_numer)); }
|
||
|
|
||
|
bool
|
||
|
has_on_boundary( const Point& p) const
|
||
|
{ CGAL_optimisation_precondition(
|
||
|
is_empty() || tco.access_dimension_d_object()( p) == d);
|
||
|
ET sqr_d = sqr_dist( p);
|
||
|
ET h_p_sqr = da_coord(p)[d] * da_coord(p)[d];
|
||
|
return (( sqr_d == h_p_sqr * sqr_i_rad_numer) ||
|
||
|
( sqr_d == h_p_sqr * sqr_o_rad_numer));}
|
||
|
|
||
|
bool
|
||
|
has_on_unbounded_side( const Point& p) const
|
||
|
{ CGAL_optimisation_precondition(
|
||
|
is_empty() || tco.access_dimension_d_object()( p) == d);
|
||
|
ET sqr_d = sqr_dist( p);
|
||
|
ET h_p_sqr = da_coord(p)[d] * da_coord(p)[d];
|
||
|
return ( ( sqr_d < h_p_sqr * sqr_i_rad_numer) ||
|
||
|
( h_p_sqr * sqr_o_rad_numer < sqr_d)); }
|
||
|
|
||
|
bool is_empty ( ) const { return number_of_points() == 0; }
|
||
|
bool is_degenerate( ) const
|
||
|
{ return ! CGAL_NTS is_positive( sqr_o_rad_numer); }
|
||
|
|
||
|
// modifiers
|
||
|
template < class InputIterator >
|
||
|
void
|
||
|
set( InputIterator first, InputIterator last)
|
||
|
{ if ( points.size() > 0) points.erase( points.begin(), points.end());
|
||
|
std::copy( first, last, std::back_inserter( points));
|
||
|
set_dimension();
|
||
|
CGAL_optimisation_precondition_msg( check_dimension(),
|
||
|
"Not all points have the same dimension.");
|
||
|
compute_min_annulus(); }
|
||
|
|
||
|
void
|
||
|
insert( const Point& p)
|
||
|
{ if ( is_empty()) d = tco.access_dimension_d_object()( p);
|
||
|
CGAL_optimisation_precondition(
|
||
|
tco.access_dimension_d_object()( p) == d);
|
||
|
points.push_back( p);
|
||
|
compute_min_annulus(); }
|
||
|
|
||
|
template < class InputIterator >
|
||
|
void
|
||
|
insert( InputIterator first, InputIterator last)
|
||
|
{ CGAL_optimisation_precondition_code( std::size_t old_n = points.size());
|
||
|
points.insert( points.end(), first, last);
|
||
|
set_dimension();
|
||
|
CGAL_optimisation_precondition_msg( check_dimension( old_n),
|
||
|
"Not all points have the same dimension.");
|
||
|
compute_min_annulus(); }
|
||
|
|
||
|
void
|
||
|
clear( )
|
||
|
{ points.erase( points.begin(), points.end());
|
||
|
compute_min_annulus(); }
|
||
|
|
||
|
// validity check
|
||
|
bool is_valid( bool verbose = false, int level = 0) const;
|
||
|
|
||
|
// traits class access
|
||
|
const Traits& traits( ) const { return tco; }
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
Traits tco; // traits class object
|
||
|
Access_coordinates_begin_d da_coord; // data accessor
|
||
|
|
||
|
Point_vector points; // input points
|
||
|
int d; // dimension of input points
|
||
|
|
||
|
ET_vector center_coords; // center of small.encl.annulus
|
||
|
|
||
|
ET sqr_i_rad_numer; // squared inner radius of
|
||
|
ET sqr_o_rad_numer; // ---"--- outer ----"----
|
||
|
ET sqr_rad_denom; // smallest enclosing annulus
|
||
|
|
||
|
Solver *solver; // linear programming solver
|
||
|
|
||
|
Index_vector inner_indices;
|
||
|
Index_vector outer_indices;
|
||
|
|
||
|
NT_matrix a_matrix; // matrix `A' of dual LP
|
||
|
NT_vector b_vector; // vector `b' of dual LP
|
||
|
NT_vector c_vector; // vector `c' of dual LP
|
||
|
|
||
|
private:
|
||
|
// squared distance to center * h_p^2 * c_d^2
|
||
|
ET sqr_dist_exact( const Point& p) const
|
||
|
{
|
||
|
ET result(0);
|
||
|
typename Access_coordinates_begin_d::Coordinate_iterator
|
||
|
p_it (da_coord ( p)); // this is p * h_p
|
||
|
ET c_d = center_coords[d];
|
||
|
ET h_p = p_it[d];
|
||
|
for (int i=0; i<d; ++i) {
|
||
|
ET x =
|
||
|
c_d * ET(p_it[i]) - /* this is c_d * p_i * h_p */
|
||
|
h_p * center_coords[i] /* this is h_p * c_i * c_d */ ;
|
||
|
result += x * x;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// the function above computes sqr_dist as ||p-c||^2
|
||
|
// (endowed with a factor of c_d^2 * h_p^2)
|
||
|
// but we know that c was computed from (possibly slightly wrong)
|
||
|
// data if NT is inexact; in order to compensate for this, let
|
||
|
// us instead compute sqr_dist as p^Tp - 2c^Tp + c^Tc, where we use
|
||
|
// the (potentially wrong) values of p^Tp and p that went into the
|
||
|
// linear program; this will give us correct containment / on_boundary
|
||
|
// checks also in the inexact-NT case.
|
||
|
ET sqr_dist( const Point& p) const
|
||
|
{
|
||
|
ET result(0), two(2);
|
||
|
NT pTp(0); // computed over input type, possibly slightly wrong
|
||
|
ET cTc(0);
|
||
|
ET two_pTc(0);
|
||
|
typename Access_coordinates_begin_d::Coordinate_iterator
|
||
|
p_it (da_coord ( p)); // this is p * h_p
|
||
|
NT h_p = p_it[d]; // input type!
|
||
|
for (int i=0; i<d; ++i) {
|
||
|
NT p_i (p_it[i]); // input type!
|
||
|
pTp += p_i * p_i; // p_i^2 * h_p^2
|
||
|
cTc += center_coords[i] * center_coords[i]; // c_i^2 * c_d^2
|
||
|
two_pTc +=
|
||
|
// 2 * c_i * c_d * p_i * h_p^2
|
||
|
2 * center_coords[i] * ET(h_p * p_i);
|
||
|
}
|
||
|
ET c_d = center_coords[d];
|
||
|
result =
|
||
|
ET(pTp) * c_d * c_d +
|
||
|
cTc * ET (h_p * h_p) +
|
||
|
- two_pTc * c_d;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// set dimension of input points
|
||
|
void
|
||
|
set_dimension( )
|
||
|
{ d = ( points.size() == 0 ? -1 :
|
||
|
tco.access_dimension_d_object()( points[ 0])); }
|
||
|
|
||
|
// check dimension of input points
|
||
|
bool
|
||
|
check_dimension( std::size_t offset = 0)
|
||
|
{ return ( std::find_if( points.begin()+offset, points.end(),
|
||
|
CGAL::compose1_1( boost::bind2nd(
|
||
|
std::not_equal_to<int>(), d),
|
||
|
tco.access_dimension_d_object()))
|
||
|
== points.end()); }
|
||
|
|
||
|
// compute smallest enclosing annulus
|
||
|
void
|
||
|
compute_min_annulus( )
|
||
|
{
|
||
|
// clear inner and outer support points
|
||
|
inner_indices.erase( inner_indices.begin(), inner_indices.end());
|
||
|
outer_indices.erase( outer_indices.begin(), outer_indices.end());
|
||
|
|
||
|
if ( is_empty()) {
|
||
|
center_coords.resize( 1);
|
||
|
sqr_i_rad_numer = -ET( 1);
|
||
|
sqr_o_rad_numer = -ET( 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( number_of_points() == 1) {
|
||
|
inner_indices.push_back( 0);
|
||
|
outer_indices.push_back( 0);
|
||
|
center_coords.resize( d+1);
|
||
|
std::copy( da_coord( points[ 0]),
|
||
|
da_coord( points[ 0])+d+1,
|
||
|
center_coords.begin());
|
||
|
sqr_i_rad_numer = ET( 0);
|
||
|
sqr_o_rad_numer = ET( 0);
|
||
|
sqr_rad_denom = ET( 1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// set up vector c and solve dual LP
|
||
|
// the ordering of the constraints is as above; the ordering
|
||
|
// of the variables is: z_p_j <-> 2 * j, y_p_j <-> 2 * j + 1
|
||
|
c_vector.resize( 2*points.size());
|
||
|
for ( int j = 0; j < number_of_points(); ++j) {
|
||
|
typename Traits::Access_coordinates_begin_d::Coordinate_iterator
|
||
|
coord_it = da_coord( points[j]);
|
||
|
NT sum = 0;
|
||
|
for ( int i = 0; i < d; ++i) {
|
||
|
sum += NT( coord_it[ i])*NT( coord_it[ i]);
|
||
|
}
|
||
|
c_vector[ 2*j ] = sum;
|
||
|
c_vector[ 2*j+1] = -sum;
|
||
|
}
|
||
|
|
||
|
LP lp (2*static_cast<int>(points.size()), d+2,
|
||
|
A_iterator ( boost::counting_iterator<int>(0),
|
||
|
A_matrix (d, da_coord, points.begin())),
|
||
|
B_iterator ( boost::counting_iterator<int>(0),
|
||
|
B_vector (d)),
|
||
|
R_iterator (CGAL::EQUAL),
|
||
|
c_vector.begin());
|
||
|
|
||
|
Quadratic_program_options options;
|
||
|
options.set_pricing_strategy(pricing_strategy(NT()));
|
||
|
delete solver;
|
||
|
solver = new Solver(lp, options);
|
||
|
CGAL_optimisation_assertion(solver->status() == QP_OPTIMAL);
|
||
|
|
||
|
// compute center and squared radius
|
||
|
ET sqr_sum = 0;
|
||
|
center_coords.resize( ambient_dimension()+1);
|
||
|
for ( int i = 0; i < d; ++i) {
|
||
|
center_coords[ i] = -solver->dual_variable( i);
|
||
|
sqr_sum += center_coords[ i] * center_coords[ i];
|
||
|
}
|
||
|
center_coords[ d] = solver->variables_common_denominator();
|
||
|
sqr_i_rad_numer = sqr_sum
|
||
|
- solver->dual_variable( d )*center_coords[ d];
|
||
|
sqr_o_rad_numer = sqr_sum
|
||
|
- solver->dual_variable( d+1)*center_coords[ d];
|
||
|
sqr_rad_denom = center_coords[ d] * center_coords[ d];
|
||
|
|
||
|
// split up support points
|
||
|
for ( int i = 0; i < solver->number_of_basic_original_variables(); ++i) {
|
||
|
int index = solver->basic_original_variable_indices_begin()[ i];
|
||
|
if ( index % 2 == 0) {
|
||
|
inner_indices.push_back( index/2);
|
||
|
} else {
|
||
|
outer_indices.push_back( index/2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template < class NT >
|
||
|
Quadratic_program_pricing_strategy pricing_strategy( NT) {
|
||
|
return QP_PARTIAL_FILTERED_DANTZIG;
|
||
|
}
|
||
|
|
||
|
Quadratic_program_pricing_strategy pricing_strategy( ET) {
|
||
|
return QP_PARTIAL_DANTZIG;
|
||
|
}
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
// Function declarations
|
||
|
// =====================
|
||
|
// I/O operators
|
||
|
template < class Traits_ >
|
||
|
std::ostream&
|
||
|
operator << ( std::ostream& os, const Min_annulus_d<Traits_>& min_annulus);
|
||
|
|
||
|
template < class Traits_ >
|
||
|
std::istream&
|
||
|
operator >> ( std::istream& is, Min_annulus_d<Traits_>& min_annulus);
|
||
|
|
||
|
// ============================================================================
|
||
|
|
||
|
// Class implementation
|
||
|
// ====================
|
||
|
|
||
|
// validity check
|
||
|
template < class Traits_ >
|
||
|
bool
|
||
|
Min_annulus_d<Traits_>::
|
||
|
is_valid( bool verbose, int level) const
|
||
|
{
|
||
|
using namespace std;
|
||
|
|
||
|
CGAL::Verbose_ostream verr( verbose);
|
||
|
verr << "CGAL::Min_annulus_d<Traits>::" << endl;
|
||
|
verr << "is_valid( true, " << level << "):" << endl;
|
||
|
verr << " |P| = " << number_of_points()
|
||
|
<< ", |S| = " << number_of_support_points() << endl;
|
||
|
|
||
|
// containment check (a)
|
||
|
// ---------------------
|
||
|
verr << " (a) containment check..." << flush;
|
||
|
|
||
|
Point_iterator point_it = points_begin();
|
||
|
for ( ; point_it != points_end(); ++point_it) {
|
||
|
if ( has_on_unbounded_side( *point_it))
|
||
|
return CGAL::_optimisation_is_valid_fail( verr,
|
||
|
"annulus does not contain all points");
|
||
|
}
|
||
|
|
||
|
verr << "passed." << endl;
|
||
|
|
||
|
// support set check (b)
|
||
|
// ---------------------
|
||
|
verr << " (b) support set check..." << flush;
|
||
|
|
||
|
// all inner support points on inner boundary?
|
||
|
Inner_support_point_iterator i_pt_it = inner_support_points_begin();
|
||
|
for ( ; i_pt_it != inner_support_points_end(); ++i_pt_it) {
|
||
|
ET h_p_sqr = da_coord (*i_pt_it)[d] * da_coord (*i_pt_it)[d];
|
||
|
if ( sqr_dist( *i_pt_it) != h_p_sqr * sqr_i_rad_numer)
|
||
|
return CGAL::_optimisation_is_valid_fail( verr,
|
||
|
"annulus does not have all inner support points on its inner boundary");
|
||
|
}
|
||
|
|
||
|
// all outer support points on outer boundary?
|
||
|
Outer_support_point_iterator o_pt_it = outer_support_points_begin();
|
||
|
for ( ; o_pt_it != outer_support_points_end(); ++o_pt_it) {
|
||
|
ET h_p_sqr = da_coord (*o_pt_it)[d] * da_coord (*o_pt_it)[d];
|
||
|
if ( sqr_dist( *o_pt_it) != h_p_sqr * sqr_o_rad_numer)
|
||
|
return CGAL::_optimisation_is_valid_fail( verr,
|
||
|
"annulus does not have all outer support points on its outer boundary");
|
||
|
}
|
||
|
/*
|
||
|
// center strictly in convex hull of support points?
|
||
|
typename Solver::Basic_variable_numerator_iterator
|
||
|
num_it = solver.basic_variables_numerator_begin();
|
||
|
for ( ; num_it != solver.basic_variables_numerator_end(); ++num_it) {
|
||
|
if ( ! ( CGAL_NTS is_positive( *num_it)
|
||
|
&& *num_it <= solver.variables_common_denominator()))
|
||
|
return CGAL::_optimisation_is_valid_fail( verr,
|
||
|
"center does not lie strictly in convex hull of support points");
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
verr << "passed." << endl;
|
||
|
|
||
|
verr << " object is valid!" << endl;
|
||
|
return( true);
|
||
|
}
|
||
|
|
||
|
// output operator
|
||
|
template < class Traits_ >
|
||
|
std::ostream&
|
||
|
operator << ( std::ostream& os,
|
||
|
const Min_annulus_d<Traits_>& min_annulus)
|
||
|
{
|
||
|
using namespace std;
|
||
|
|
||
|
typedef typename Min_annulus_d<Traits_>::Point Point;
|
||
|
typedef ostream_iterator<Point> Os_it;
|
||
|
typedef typename Traits_::ET ET;
|
||
|
typedef ostream_iterator<ET> Et_it;
|
||
|
|
||
|
switch ( CGAL::get_mode( os)) {
|
||
|
|
||
|
case CGAL::IO::PRETTY:
|
||
|
os << "CGAL::Min_annulus_d( |P| = "
|
||
|
<< min_annulus.number_of_points() << ", |S| = "
|
||
|
<< min_annulus.number_of_inner_support_points() << '+'
|
||
|
<< min_annulus.number_of_outer_support_points() << endl;
|
||
|
os << " P = {" << endl;
|
||
|
os << " ";
|
||
|
copy( min_annulus.points_begin(), min_annulus.points_end(),
|
||
|
Os_it( os, ",\n "));
|
||
|
os << "}" << endl;
|
||
|
os << " S_i = {" << endl;
|
||
|
os << " ";
|
||
|
copy( min_annulus.inner_support_points_begin(),
|
||
|
min_annulus.inner_support_points_end(),
|
||
|
Os_it( os, ",\n "));
|
||
|
os << "}" << endl;
|
||
|
os << " S_o = {" << endl;
|
||
|
os << " ";
|
||
|
copy( min_annulus.outer_support_points_begin(),
|
||
|
min_annulus.outer_support_points_end(),
|
||
|
Os_it( os, ",\n "));
|
||
|
os << "}" << endl;
|
||
|
os << " center = ( ";
|
||
|
copy( min_annulus.center_coordinates_begin(),
|
||
|
min_annulus.center_coordinates_end(),
|
||
|
Et_it( os, " "));
|
||
|
os << ")" << endl;
|
||
|
os << " squared inner radius = "
|
||
|
<< min_annulus.squared_inner_radius_numerator() << " / "
|
||
|
<< min_annulus.squared_radii_denominator() << endl;
|
||
|
os << " squared outer radius = "
|
||
|
<< min_annulus.squared_outer_radius_numerator() << " / "
|
||
|
<< min_annulus.squared_radii_denominator() << endl;
|
||
|
break;
|
||
|
|
||
|
case CGAL::IO::ASCII:
|
||
|
copy( min_annulus.points_begin(), min_annulus.points_end(),
|
||
|
Os_it( os, "\n"));
|
||
|
break;
|
||
|
|
||
|
case CGAL::IO::BINARY:
|
||
|
copy( min_annulus.points_begin(), min_annulus.points_end(),
|
||
|
Os_it( os));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
CGAL_optimisation_assertion_msg( false,
|
||
|
"CGAL::get_mode( os) invalid!");
|
||
|
break; }
|
||
|
|
||
|
return( os);
|
||
|
}
|
||
|
|
||
|
// input operator
|
||
|
template < class Traits_ >
|
||
|
std::istream&
|
||
|
operator >> ( std::istream& is, CGAL::Min_annulus_d<Traits_>& min_annulus)
|
||
|
{
|
||
|
using namespace std;
|
||
|
|
||
|
switch ( CGAL::get_mode( is)) {
|
||
|
|
||
|
case CGAL::IO::PRETTY:
|
||
|
cerr << endl;
|
||
|
cerr << "Stream must be in ascii or binary mode" << endl;
|
||
|
break;
|
||
|
|
||
|
case CGAL::IO::ASCII:
|
||
|
case CGAL::IO::BINARY:
|
||
|
typedef typename CGAL::Min_annulus_d<Traits_>::Point Point;
|
||
|
typedef istream_iterator<Point> Is_it;
|
||
|
min_annulus.set( Is_it( is), Is_it());
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
CGAL_optimisation_assertion_msg( false, "CGAL::IO::mode invalid!");
|
||
|
break; }
|
||
|
|
||
|
return( is);
|
||
|
}
|
||
|
|
||
|
} //namespace CGAL
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
# pragma warning(pop)
|
||
|
#endif
|
||
|
|
||
|
#endif // CGAL_MIN_ANNULUS_D_H
|
||
|
|
||
|
// ===== EOF ==================================================================
|