khachiyan_approximation.h

来自「很多二维 三维几何计算算法 C++ 类库」· C头文件 代码 · 共 632 行 · 第 1/2 页

H
632
字号
// Copyright (c) 1997-2001  ETH Zurich (Switzerland).// All rights reserved.//// This file is part of CGAL (www.cgal.org); you may redistribute it under// the terms of the Q Public License version 1.0.// See the file LICENSE.QPL distributed with CGAL.//// 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: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.3-branch/Approximate_min_ellipsoid_d/include/CGAL/Approximate_min_ellipsoid_d/Khachiyan_approximation.h $// $Id: Khachiyan_approximation.h 36397 2007-02-16 15:56:11Z gaertner $// //// Author(s)     : Kaspar Fischer <fischerk@inf.ethz.ch>// Note: whenever a comment refers to "Khachiyan's paper" then the// paper "Rounding of polytopes in the real number model of// computation" is ment (Mathematics of Operations Research, Vol. 21,// No. 2, May 1996).  Nontheless, most comments refer to the// accompanying documentation sheet (and not to the above paper), see// the file(s) in documentation/.#ifndef CGAL_KHACHIYAN_APPROXIMATION_H#define CGAL_KHACHIYAN_APPROXIMATION_H#include <CGAL/Interval_arithmetic.h>#include <CGAL/Approximate_min_ellipsoid_d/Approximate_min_ellipsoid_d_configure.h>// If CGAL is not being used, we need to define certain things:#ifndef CGAL_VERSION  namespace CGAL {    struct Tag_true {};    struct Tag_false {};  }#endifnamespace CGAL {  // In order not to pollute the CGAL namespace with implementation  // fuzz, we put such things into the following attic:  namespace Appel_impl {    template<typename FT>    struct Selector {      typedef Tag_true Is_exact;    };        template<>    struct Selector<float> {      typedef Tag_false Is_exact;    };      template<>    struct Selector<double> {      typedef Tag_false Is_exact;    };  } // namespace Appel_impl  template<bool Embed,class Traits>  class Khachiyan_approximation {  public: // types:    typedef typename Traits::FT FT;    typedef typename Traits::ET ET;    typedef typename Traits::Point Point;    typedef typename Traits::Cartesian_const_iterator C_it;    typedef typename Appel_impl::Selector<FT>::Is_exact Exact_flag;      private: // member variables and invariants for them:    Traits& tco;                    // traits class object    std::vector<const Point *> P;   // input points    int n;                          // number of input points, i.e., P.size()    // This class comes in two flavours:    //    //  (i) When Embed is false, the input points are taken to be    //    ordinary points in R^{d_P}, where d_P is the dimension of the    //    input points (as obtained by Traits::dimension()).  In this    //    case the "dimension of the ambient space" (see variable d    //    below) simply equals d_P.    //    //  (ii) When Embed is true, the input points are embedded by    //    means of p -> (p,1) into R^{d_P+1}.  In this case, the    //    dimension of the amient space is d_P+1.    //    // Notice that (at almost all places in this code) whenever a    // point "p" is mentioned in a comment then the embedded point is    // ment in case (ii).    const int d_P;                  // dimension of the input points    const int d;                    // dimension of the ambient space    // An instance of this class will compute (provided the points are    // not degenerate, see is_degenerate() below) a solution x to    // Khachiyan's program (D) such that the "relaxed optimality    // conditions"    //    //      p_i^T M(x)^{-1} p_i <= (1+desired_eps) d             (*)    //    // hold for all input points p_i.  (Notice that the p_i in this    // case are the embedded points when Embed is true.)  Khachiyan's    // lemma then guarantees that the ellipsoid defined by the matrix    // M(x)^{-1} is a "good" approximation in some sense (for further    // details on "good" refer to is_valid() below).    //    // The current epsilon for which (*) holds is stored in the    // following variable; the variable is only defined if is_deg is    // false.    FT eps;    // In addition to the current epsilon (see variable eps) above, we    // also maintain the exact epsilon for which (*) holds; this exact    // epsilon is eps_exact. (Notice that (*) for the variable eps    // only holds modulo rounding-errors if FT is an inexact type.)    // In order not to unnecessarily recompute eps_exact, we keep the    // flag is_exact_eps_uptodate which is true if and only if the    // value in eps_exact is correct.    double eps_exact;    bool is_exact_eps_uptodate;        // Khachian's algorithm is only guaranteed to work when the input    // points linearly span the whole space (i.e., if dim(span(P)) =    // d, or, equivalently, when the smallest enclosing ellipsoid has    // a volume > 0).  The user of this class must call    // is_degenerate() which returns false iff dim(span(P)) = d;    // is_degenerate() simply looks up the following variable:    bool is_deg;    #ifdef CGAL_APPEL_ASSERTION_MODE    // We store the current solution x to program (D) as a n-vector.    // If is_deg is true, the variable x has no meaning.    std::vector<FT> x;    #endif // CGAL_APPEL_ASSERTION_MODE        // The inverse of the (d x d)-matrix M(x) is stored in the    // variable mi.  The element (M(x)^{-1})_{ij} is stored in    // mi[i+j*d].  (It would actually be enough to only store the    // upper half of the matrix since it's symmetric -- but we don't    // care.)  If is_deg is true, the variable mi is undefined.    //    // Todo: optimize by only storing one half of mi.    std::vector<FT> mi;        // The following variable sum is used to build, during the    // initialization phase, the (d x d)-matrix M(x).  The variable is    // only defined if is_deg is true; once is_deg becomes false, the    // variable sum isn't used any more (except in routines for    // debugging and statistics).  If is_deg is true then sum represents    // the martix    //    //    sum(p_i p_i^T,i=0..(n-1));    //    // the (i,j)th element of which is stored as sum[i+j*d].  (It    // would actually be enough to only store the upper half of the    // matrix since it's symmetric -- but we don't care.)    //    // Todo: optimize by only storing one half of sum.    std::vector<FT> sum;    // Khachiyan's algorithm heavily relies on the excess ex[i] of    // input point P[i] w.r.t. the current solution x (see routine    // excess() below), i in {0,...,n-1}.  For the first iteration, we    // compute these numbers explicitly, which is quite costly, namely    // O(d^2) per excess, for a total of O(n d^2).  However, in all    // remaining iterations we only update the excesses which can be    // done in O(n d).  For this we need the excesses of the previous    // round, and that's why we store them.  Thus, if is_deg is false    // then ex[i] is the excess of point P[i] w.r.t. the current    // solution x:    std::vector<FT> ex;    // We remember the index (into ex) of a largest element in ex: If    // is_deg is false then    //    //    ex_max = argmax_{0 <= i < n} ex[i].    //    int ex_max;     // We sometimes need temporary storage.  We allocate this once at    // instance construction time:    mutable std::vector<FT> tmp;    mutable std::vector<FT> t;  public: // member variables for statistics    #ifdef CGAL_APPEL_STATS_MODE    // The value max_error_m is the maximal absolute value in an    // entry of (M(x) M(x)^{-1} - I), where I is the identity matrix.    // Of course, in theory, M(x) M(x)^{-1} - I should be the zero    // matrix, but if FT is an inexact type (i.e., double), then this    // need not be the case anymore, due to rounding errors.    //    // The variable max_error_m_all is the maximum over all    // max_error values ever encountered during the computation.    FT max_error_m, max_error_m_all;    #endif // CGAL_APPEL_STATS_MODE  private: // internal helper routines:        template<typename NumberType, typename InputIterator>    NumberType excess(InputIterator p)      // Computes p^T M(x)^{-1} p, where x is the current solution.      // (When Embed is true, then the p in the previous sentence's      // formula is an embedded point...)      //      // Complexity: O(d^2)      //      // Note: this routine is parametrized by a number-type argument      // because we will use it in exact_epsilon() and is_valid()      // below with the exact number-type ET (which may be different      // from the possibly inexact number-type FT).    {      typedef NumberType NT;      NT result(0);      InputIterator qi(p);            for (int i=0; i<d; ++i) {	// compute i-th entry of the vector M(x)^{-1} p into tmp:	NT tmp(0);	InputIterator q(p);	for (int j=0; j<d_P; ++j, ++q)	  tmp += NT(*q) * NT(mi[i+j*d]);	if (Embed)	  tmp += NT(mi[i+d_P*d]);	// add tmp*p_i to result:	if (!Embed || i < d_P) {	  result += NT(*qi) * NT(tmp);	  ++qi;	} else	  result += NT(tmp);      }      return result;    }    void update_sum(int start)      // Adds to the variable sum the matrices P[i] P[i]^T where i      // ranges from start to n-1.      //      // Complexity: O((n-start) d^2)      //      // Todo: maybe use something like Kahan Summation here? See      // <http://en.wikipedia.org/wiki/Kahan_Summation_Algorithm>.    {      for (int k=start; k<n; ++k) {	C_it pi = tco.cartesian_begin(*P[k]);	for (int i=0; i<d_P; ++i, ++pi) {	  C_it pj = tco.cartesian_begin(*P[k]);	  for (int j=0; j<d_P; ++j, ++pj)	    sum[i+j*d] += (*pi) * (*pj);	  if (Embed)	    sum[i+d_P*d] += (*pi);	}	if (Embed) {	  C_it pj = tco.cartesian_begin(*P[k]);	  for (int j=0; j<d_P; ++j, ++pj)	    sum[d_P+j*d] += (*pj);	  sum[d_P+d_P*d] += 1;	}      }    }  public: // construction & destruction:    Khachiyan_approximation(int dim,int n_est,Traits& tco) :      // An instance of this class represents, for some given point      // set P, a solution x to program (D) which satisfies the      // relaxed optimality conditions (*) for some desired_epsilon      // (to be specified by the user at a later point).  After      // calling this constructor, the instance will represent the      // (degenerate) solution to program (D) for P={}.  By adding      // points to P (see routine add()), you can compute an      // approximate solution to (D) for any point set P.      //      // (A solution does not always exist; the set P must linearly      // span the whole space in order for a solution to exist.  See      // routine is_degenerate() and the result of routine add() for      // more information.)      //      // The number n_est is an estimate on the number of points you      // are going to add; n_est need not coincide with the actual      // number of points you will eventually have added.  It merely      // gives a hint on how much storage is needed.      tco(tco), n(0),      d_P(dim), d(Embed? d_P+1 : d_P), is_deg(true),      #ifdef CGAL_APPEL_ASSERTION_MODE       x(n_est),      #endif // CGAL_APPEL_ASSERTION_MODE      mi(d*d), sum(d*d), ex(n_est), tmp(d), t(d*d)    {      CGAL_APPEL_LOG("appel","Entering Khachiyan with d=" << d << " (" <<		(Embed? "" : "not ") << "embedded)." << std::endl);      CGAL_APPEL_TIMER_START("khachiyan");      // In order to satisfy the invariant on m, we have to initalize      // m with the zero matrix:      for (int i=0; i<d; ++i)	for (int j=0; j<d; ++j)	  sum[i+j*d] = FT(0);    }        ~Khachiyan_approximation();    template<typename InputIterator>    bool add(InputIterator first,InputIterator last,double desired_eps)      // Adds the points from the range [first,last) to the instance's      // set P and computes a solution x to program (D) satisfying the      // relaxed optimality conditions (*) for the given value      // desired_eps.  (Khachiyan's lemma then guarantees that the      // ellipsoid defined by the matrix M(x)^{-1} is a "good"

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?