khachiyan_approximation_impl.h
来自「很多二维 三维几何计算算法 C++ 类库」· C头文件 代码 · 共 666 行 · 第 1/2 页
H
666 行
// 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_impl.h $// $Id: Khachiyan_approximation_impl.h 37145 2007-03-16 08:27:32Z afabri $// //// 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/.#include <numeric>#include <CGAL/tags.h>#include <CGAL/Approximate_min_ellipsoid_d/Khachiyan_approximation.h>namespace CGAL { namespace Appel_impl { // Computes the inverse of the positive definite (dxd)-matrix A // into Ai, by using the Cholesky decomposition of A. All three // iterator template parameters must be random access iterators of // value type FT. The iterator tmp must have d entries. The // routine returns true in case no errors occurred; false is // returned in case of stability problems or when A is not // positive definite. // // Note: A is destroyed in this process. // // Precondition: A and Ai may point to the same matrix (i.e., might alias). template<typename FT, typename Tmp_iterator, typename A_iterator, typename A_inverse_iterator> bool pd_matrix_inverse(const int d, A_iterator A, A_inverse_iterator Ai, Tmp_iterator tmp) { // I use the following version (in matlab notation) from Walter // Gander's lecture "Ausgleichsrechnung" (ETH Zurich, around 2000, see // http://www.inf.ethz.ch/personal/chinella/education/ausgleich/ // Demo2.txt): // // # compute lower-triangular L s.t. A = LL^T: // for j=1:d // v = A(j:d,j) - L(j:d,1:j-1) * L(j,1:j-1)'; // L(j:d,j) = v/sqrt(v(1)); // end; // // Observe that the vector v in this pseudo-code is a // (d-j+1)-vector; we use (the last d-j+1 entries of) the vector // tmp to store v. (Also, the above program uses one-based // counting, the code below is of course zero-based.) Note also // that instead of computing the result into L we can as well // overwrite the lower part of A. for (int j=0; j<d; ++j) { // compute new column (v in above pseudo-code): for (int i=j; i<d; ++i) { FT ll(0); for (int k=0; k<j; ++k) ll += A[i+k*d] * A[j+k*d]; tmp[i] = A[i+j*d] - ll; } // check regularity: if (tmp[j] <= 0) // todo: epsilon? return false; // overwrite column: const FT scale = FT(1)/std::sqrt(tmp[j]); for (int i=j; i<d; ++i) A[i+j*d] = tmp[i] * scale; } // Now that we have in the lower triangular part of A the // Cholesky decomposition A = LL^T of the original A, we compute // the inverse of A see "Numerical Recipes in C", end of Chapter // 2.9. for (int i=0; i<d; ++i) { A[i+i*d] = FT(1)/A[i+i*d]; for (int j=i+1; j<d; ++j) { FT sum(0); for (int k=i; k<j; ++k) sum -= A[j+k*d] * A[k+i*d]; A[j+i*d] = sum/A[j+j*d]; } } // Finally, we calculate A^{-1} = (L^{-1})^T L^{-1} into Ai: for (int i=0; i<d; ++i) for (int j=0; j<=i; ++j) { // compute entry (i,j) of A^{-1}: FT sum(0); for (int k=i; k<d; ++k) sum += A[k+i*d] * A[k+j*d]; Ai[i+j*d] = sum; // Since A^{-1} is symmetric, we set: Ai[j+i*d] = sum; } return true; } } // end of namespace Appel_impl template<bool Embed,class Traits> Khachiyan_approximation<Embed,Traits>:: ~Khachiyan_approximation() { CGAL_APPEL_ASSERT_EXPENSIVE(is_valid(false)); } #ifdef CGAL_APPEL_ASSERTION_MODE template<bool Embed,class Traits> void Khachiyan_approximation<Embed,Traits>::compute_M_of_x() // Computes into t the matrix // // M(x) = sum(x_i p_i p_i^T,i=0..(n-1)). // // Complexity: O(n d^2) // // Note: we only use this routine to check assertions. { // erase m: for (int i=0; i<d; ++i) for (int j=0; j<d; ++j) t[i+j*d] = FT(0); // evaluate products: for (int k=0; 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) t[i+j*d] += x[k] * (*pi) * (*pj); if (Embed) t[i+d_P*d] += x[k] * (*pi); } if (Embed) { C_it pj = tco.cartesian_begin(*P[k]); for (int j=0; j<d_P; ++j, ++pj) t[d_P+j*d] += x[k] * (*pj); t[d_P+d_P*d] += x[k]; } } } #endif // CGAL_APPEL_ASSERTION_MODE template<bool Embed,class Traits> bool Khachiyan_approximation<Embed,Traits>:: compute_inverse_of_t_into_mi(const Tag_true exact) // Note: this routine is not used in CGAL; it turned out that using // exact arithmetic, the algorithm is very slow. { // We need to compute into mi the inverse of the matrix t. We use // Gauss-Jordan elimination to do this. So we write t and the // identity matrix I as [I|t] and transform this (by means of row // operations) into a system of the form [N|I]. Then N is the // inverse of t. Why? This is like solving a linear system with // several right-hand sides simultaneously by Gauss elimination: // Since the transformations we apply do not change the solution // space of the intermediate systems, we can say: The system t x = // e_j has, for any i in {1,...,d}, the same solution space as I x // = n_i (with n_i being the i-th colum of N); it follows that // x=n_i. // store the identity matrix in mi: for (int i=0; i<d; ++i) for (int j=0; j<d; ++j) mi[i+j*d] = FT(i==j? 1 : 0); // Since it is not always possible to achieve a final form [*|I] // without row exchanges, we try to get the form [*|P(I)] where // P(I) stands for a permutation of the rows of the matrix I --- // in other words: we want a "1" in every row. Therefore, we // maintain a integer for every row with the number of the column // into which we placed a "1", or a -1 in case we haven't yet // place a "1" in this row. Also, we maintain for every column // the number of the row into which we placed the one. std::vector<int> col_with_one(d,-1); std::vector<int> row_with_one(d); for (int j=d-1; j>=0; --j) { // In this iteration of the loop we try to make the column j of // m a zero vector with exactly one 1 in an unused place (i.e., // in a place k for which one_in_row(k) isn't yet true). // search for a suitable place k: bool found = false; int k = d-1; for (; k>=0; --k) if (!is_zero(t[k+j*d]) && col_with_one[k]<0) { found = true; break; } if (!found) return false; col_with_one[k] = j; row_with_one[j] = k; // zero out the entries above and below entry k: for (int i=0; i<d; ++i) if (i != k) { // we add l times row k to row i: const FT l = -t[i+j*d]/t[k+j*d]; for (int jj=0; jj<d; ++jj) mi[i+jj*d] += l*mi[k+jj*d]; for (int jj=0; jj<=j; ++jj) t[i+jj*d] += l*t[k+jj*d]; } // finally, we scale row k to get a one at (k,j): for (int jj=0; jj<d; ++jj) mi[k+jj*d] /= t[k+j*d]; for (int jj=0; jj<=j; ++jj) t[k+jj*d] /= t[k+j*d]; } // At this point we know that for any i in {1,...,d} the system m // x = e_i has the some solution as P(I) x = n_i. So x = // P(I)^{-1} n_i and it thus suffices to undo the permutation: for (int i=0; i<d; ++i) if (row_with_one[i] != i) { const int repl_row = row_with_one[i]; const int col = col_with_one[i]; for (int j=0; j<d; ++j) std::swap(mi[i+j*d],mi[repl_row+j*d]); row_with_one[col] = repl_row; col_with_one[repl_row] = col; row_with_one[i] = col_with_one[i] = i; } return true; } template<bool Embed,class Traits> bool Khachiyan_approximation<Embed,Traits>:: compute_inverse_of_t_into_mi(const Tag_false /* exact */) { // handle the obvious case when the points cannot span \R^d: if (P.size() <= static_cast<unsigned int>(d)) return false; return Appel_impl::pd_matrix_inverse<FT>(d, t.begin(), mi.begin(), tmp.begin()); } template<bool Embed,class Traits> bool Khachiyan_approximation<Embed,Traits>:: compute_initial_inverse_from_sum() { // assertions: CGAL_APPEL_ASSERT(is_deg); // check number of points: if (n == 0) return false; // When this routine is called, the variable sum contains the // matrix sum(p_i p_i^T,i=0...(n-1)), which coincides with n M(x) // for x = (1/n,...,1/n). Our aim is to compute M(x)^{-1} into // the variable mi and, if the latter matrix exits, to set x to // (1/n,...,1/n). For this, we first compute M(x) into variable t: const FT invOfn = 1/FT(n); for (int i=0; i<d; ++i) for (int j=0; j<d; ++j) t[i+j*d] = sum[i+j*d] * invOfn; if (!compute_inverse_of_t_into_mi(Exact_flag())) return false; #ifdef CGAL_APPEL_ASSERTION_MODE // We start with the solution (1/n,...,1/n): for (int k=0; k<n; ++k) x[k] = invOfn; #endif // CGAL_APPEL_ASSERTION_MODE // Finally, we compute the excess of P[k] (w.r.t. x) into ex[k] // for all k: ex_max = 0; for (int k=0; k<n; ++k) if ((ex[k] = excess<FT>(tco.cartesian_begin(*P[k]))) > ex[ex_max]) ex_max = k; CGAL_APPEL_LOG("appel"," Largest excess after initialization is " << to_double(ex[ex_max]) << "." << "\n"); // Accoding to Khachiyam (Lemma 3, eq. (2.20) in "Rounding of // polytopes in the real number model of computation"), the // following eps makes (*) hold: eps = n-1; is_exact_eps_uptodate = false; return true; } #ifdef CGAL_APPEL_ASSERTION_MODE template<bool Embed,class Traits> typename Traits::FT Khachiyan_approximation<Embed,Traits>:: representation_error_of_mi() { using std::abs; // If the points are degenerate then the inverse of M(x) doesn't // exist, and so we exit immediately: if (is_deg) return FT(0); // compute M(x) into the matrix represented by t: compute_M_of_x(); // compute mi times the matrix M(x) (which should give the // identity matrix): FT max_error(0);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?