1 // Copyright (c) 2006-2009,2013 John Abbott 2 // Main authors: Laura Torrente (assisted by John Abbott) 3 4 // This file is part of the source of CoCoALib, the CoCoA Library. 5 6 // CoCoALib is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 11 // CoCoALib is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // You should have received a copy of the GNU General Public License 17 // along with CoCoALib. If not, see <http://www.gnu.org/licenses/>. 18 19 20 #include "CoCoA/ApproxPts.H" 21 22 #include "CoCoA/BigRat.H" 23 #include "CoCoA/RingTwinFloat.H" 24 #include "CoCoA/assert.H" 25 #include "CoCoA/convert.H" 26 #include "CoCoA/error.H" 27 #include "CoCoA/ring.H" 28 #include "CoCoA/utils.H" 29 30 #include <algorithm> 31 using std::find_if; 32 #include <cmath> 33 // using std::log; // only in UnitVolBallRadius !! Shadowed by the CoCoALib fn log(BigInt) !! 34 // using std::exp; // only in UnitVolBallRadius 35 #include <list> 36 using std::list; 37 //#include <vector> 38 using std::vector; 39 40 41 namespace CoCoA 42 { 43 44 typedef vector<double> PointDbl; 45 46 namespace // anonymous namespace for file local auxiliary functions and definitions. 47 { 48 49 // The square of x -- useful if x is a big expression. square(double x)50 inline double square(double x) 51 { 52 return x*x; 53 } 54 55 56 // This is not really specific to approximate points, but where else should it go? 57 // Compute radius of D-ball having volume 1. 58 // (I got the formula from Wikipedia under "hypersphere") UnitVolBallRadius(long D)59 double UnitVolBallRadius(long D) 60 { 61 using std::log; 62 using std::exp; 63 CoCoA_ASSERT(D < 10000); // disallow very high dimensions 64 const double pi = 4*atan(1.0); 65 double logfactor = (D/2)*(-log(2*pi)); // D/2 exploits integer division! NB -(D/2)*log(...) does NOT WORK. 66 if (D & 1) 67 logfactor -= log(2.0); 68 for (long k=D; k > 1; k -= 2) 69 logfactor += log(double(k)); 70 return exp(logfactor/D); 71 } 72 73 74 // Compute square of 2-norm between P and Q. L2NormSq(const PointDbl & P,const PointDbl & Q)75 double L2NormSq(const PointDbl& P, const PointDbl& Q) 76 { 77 CoCoA_ASSERT(len(P) == len(Q)); 78 double SumSq = 0; 79 const long dim = len(P); 80 for (long i=0; i < dim; ++i) 81 SumSq += square(P[i]-Q[i]); 82 return SumSq; 83 } 84 85 86 // Data structure for representing PreProcessed Points. 87 struct PPP 88 { 89 public: // data members 90 PointDbl myCoords; // the coords of the preprocessed point -- average of myOrigPts 91 long myNumOrigPts; // length of list myOrigPts (used only in PreprocessSubdivAlgm) 92 list<long> myOrigPts; // indices into a master table 93 vector<double> myDistanceSq; // used only in PreprocessSubdivAlgm 94 long myWorstOrigPt; // the index of the farthest point 95 }; 96 97 98 // Returns the average of a set of PointDbls; the set is indicated by the first arg 99 // which is a list of indices into the second arg. SubsetAve(const list<long> & IndexList,const vector<PointDbl> & pts)100 PointDbl SubsetAve(const list<long>& IndexList, const vector<PointDbl>& pts) 101 { 102 CoCoA_ASSERT(!pts.empty()); 103 const long dim = len(pts[0]); 104 PointDbl CofG(dim); 105 // for (list<long>::const_iterator it=IndexList.begin(); it != IndexList.end(); ++it) 106 for(long i: IndexList) 107 { 108 const PointDbl& P = pts[i]; 109 for (long j=0; j < dim; ++j) 110 CofG[j] += P[j]; 111 } 112 113 const long n = len(IndexList); 114 for (long j=0; j < dim; ++j) 115 CofG[j] /= n; 116 return CofG; 117 } 118 119 SubsetAve(const list<long> & IndexList,const vector<ApproxPts::PointR> & pts)120 ApproxPts::PointR SubsetAve(const list<long>& IndexList, const vector<ApproxPts::PointR>& pts) 121 { 122 CoCoA_ASSERT(!pts.empty()); 123 const long dim = len(pts[0]); 124 ApproxPts::PointR ave(dim, zero(owner(pts[0][0]))); 125 // for (list<long>::const_iterator it=IndexList.begin(); it != IndexList.end(); ++it) 126 for (long i: IndexList) 127 { 128 const ApproxPts::PointR& P = pts[i]; 129 for (long j=0; j < dim; ++j) 130 ave[j] += P[j]; 131 } 132 133 const long n = len(IndexList); 134 for (long j=0; j < dim; ++j) 135 ave[j] /= n; 136 return ave; 137 } 138 139 140 // Choose a suitable positive value for any tolerance component which is zero, 141 // and replace that component by the chosen postive value. ZeroToleranceHack(const vector<ApproxPts::PointR> & OrigPts,vector<RingElem> & tolerance)142 void ZeroToleranceHack(const vector<ApproxPts::PointR>& OrigPts, vector<RingElem>& tolerance) 143 { 144 const long dim = len(tolerance); 145 const long NumPts = len(OrigPts); 146 for (long i=0; i < dim; ++i) 147 { 148 if (tolerance[i] != 0) continue; 149 150 RingElem MinDist = tolerance[i]; // really just assignment to zero. 151 for (long j=0; j < NumPts; ++j) 152 for (long k=j+1; k < NumPts; ++k) 153 { 154 const RingElem d = abs(OrigPts[j][i]-OrigPts[k][i]); 155 if (MinDist == 0 || d < MinDist) 156 MinDist = d; 157 } 158 if (MinDist != 0) 159 tolerance[i] = MinDist/4; 160 else 161 tolerance[i] = 1; 162 } 163 } 164 165 166 // Rescale coords of pt by factors in array ScaleFactor. ScalePt(PointDbl & ScaledPt,const ApproxPts::PointR & OrigPt,const vector<RingElem> & ScaleFactor)167 void ScalePt(PointDbl& ScaledPt, const ApproxPts::PointR& OrigPt, const vector<RingElem>& ScaleFactor) 168 { 169 CoCoA_ASSERT(len(OrigPt) > 0); 170 CoCoA_ASSERT(len(OrigPt) == len(ScaleFactor)); 171 const long dim = len(OrigPt); 172 ScaledPt.resize(dim); 173 BigRat Q; 174 RingElem coord(owner(OrigPt[0])); 175 for (long i=0; i < dim; ++i) 176 { 177 coord = OrigPt[i] * ScaleFactor[i]; 178 if (!IsRational(Q, coord) || !IsConvertible(ScaledPt[i], Q)) 179 CoCoA_ERROR("Cannot convert to double", "ScalePt"); 180 } 181 } 182 183 // Rescale the OrigPts and convert them into PointDbl NormalizePts(vector<PointDbl> & ScaledPts,const vector<ApproxPts::PointR> & OrigPts,const vector<RingElem> & tolerance)184 void NormalizePts(vector<PointDbl>& ScaledPts, const vector<ApproxPts::PointR>& OrigPts, const vector<RingElem>& tolerance) 185 { 186 const long NumPts = len(OrigPts); 187 const long dim = len(tolerance); 188 189 ring R = owner(tolerance[0]); 190 vector<RingElem> InverseTolerance(dim, zero(R)); 191 for (long j=0; j < dim; ++j) 192 InverseTolerance[j] = 1/tolerance[j]; 193 194 ScaledPts.resize(NumPts); 195 for (long i=0; i < NumPts; ++i) 196 ScalePt(ScaledPts[i], OrigPts[i], InverseTolerance); 197 } 198 199 200 ///////////////////////////////////////////////////////////////////////////// 201 // Auxiliary definitions for the grid algorithm. 202 GridNearPoint(const PointDbl & P)203 PointDbl GridNearPoint(const PointDbl& P) 204 { 205 const long dim = len(P); 206 PointDbl GNP(dim); 207 for (long i=0; i < dim; ++i) 208 GNP[i] = 2*round(P[i]/2); 209 return GNP; 210 } 211 212 213 // Only used in GridAlgm as arg to std::find. 214 // WARNING data member is a reference!! 215 class CoordsEq 216 { 217 public: CoordsEq(const PointDbl & P)218 CoordsEq(const PointDbl& P): myPointDbl(P) {}; operator()219 bool operator()(const PPP& PPPt) { return myPointDbl == PPPt.myCoords; }; 220 private: // data members 221 const PointDbl& myPointDbl; 222 }; 223 224 225 ///////////////////////////////////////////////////////////////////////////// 226 // Functions for aggregative algorithm 227 228 // ave = (w1*P1 + w2*P2)/(w1+w2); WeightedAve(PointDbl & ave,double w1,const PointDbl & P1,double w2,const PointDbl & P2)229 void WeightedAve(PointDbl& ave, double w1, const PointDbl& P1, double w2, const PointDbl& P2) 230 { 231 const long dim = len(ave); 232 CoCoA_ASSERT(len(P1) == dim && len(P2) == dim); 233 CoCoA_ASSERT(w1+w2 != 0); 234 for (long i=0; i < dim; ++i) 235 { 236 ave[i] = (w1*P1[i]+w2*P2[i])/(w1+w2); 237 } 238 } 239 240 241 struct NearPair 242 { 243 public: NearPairNearPair244 NearPair(long i, long j, double sepsq): myLowerIndex(i), myHigherIndex(j), mySepSq(sepsq) {}; 245 public: // data members 246 const long myLowerIndex, myHigherIndex; 247 double mySepSq; 248 }; 249 250 251 // Used only as arg to std::sort algorithm LessNearPair(const NearPair & P1,const NearPair & P2)252 inline bool LessNearPair(const NearPair& P1, const NearPair& P2) 253 { 254 return P1.mySepSq < P2.mySepSq; 255 } 256 257 258 // Used in call to algorithm std::remove_if. 259 class NearPairContains 260 { 261 public: NearPairContains(long j)262 NearPairContains(long j): myIndex(j) {} operator()263 bool operator()(const NearPair& P) { return P.myLowerIndex == myIndex || P.myHigherIndex == myIndex; } 264 private: // data member 265 const long myIndex; 266 }; 267 268 269 270 271 ///////////////////////////////////////////////////////////////////////////// 272 // Below is auxiliary code for the "subdiv" algorithm. 273 274 // Fill in data member myDistanceSq of X ComputeDistSq(PPP & X,const vector<PointDbl> & OrigPts)275 void ComputeDistSq(PPP& X, const vector<PointDbl>& OrigPts) 276 { 277 for (long i=0; i < len(OrigPts); ++i) 278 X.myDistanceSq[i] = L2NormSq(X.myCoords, OrigPts[i]); 279 280 double WorstDist = 0; 281 // for (list<long>::const_iterator it = X.myOrigPts.begin(); it != X.myOrigPts.end(); ++it) 282 for (long i: X.myOrigPts) 283 if (X.myDistanceSq[i] >= WorstDist) 284 { 285 WorstDist = X.myDistanceSq[i]; 286 X.myWorstOrigPt = i; 287 } 288 } 289 290 291 // Make Y a copy of X but without the i-th OrigPt. EraseAndUpdate(PPP & Y,const PPP & X,long i,const vector<PointDbl> & V)292 void EraseAndUpdate(PPP& Y, const PPP& X, long i, const vector<PointDbl>& V) 293 { 294 Y.myOrigPts = X.myOrigPts; 295 Y.myOrigPts.remove(i); 296 Y.myNumOrigPts = X.myNumOrigPts-1; 297 Y.myCoords = SubsetAve(Y.myOrigPts, V); 298 ComputeDistSq(Y, V); 299 } 300 301 302 // Make Y a copy of X with the i-th OrigPt added in. AddAndUpdate(PPP & Y,const PPP & X,long i,const vector<PointDbl> & V)303 void AddAndUpdate(PPP& Y, const PPP& X, long i, const vector<PointDbl>& V) 304 { 305 Y.myOrigPts = X.myOrigPts; 306 Y.myOrigPts.push_back(i); 307 Y.myNumOrigPts = X.myNumOrigPts+1; 308 Y.myCoords = SubsetAve(Y.myOrigPts, V); 309 ComputeDistSq(Y, V); 310 } 311 312 313 // Redistribute the assignments of OrigPts to PPPts to minimize the sum of squares of distances redistribute(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts)314 void redistribute(vector<PPP>& PPPts, const vector<PointDbl>& OrigPts) 315 { 316 CoCoA_ASSERT(len(OrigPts) > 0); 317 const long NumPPP = len(PPPts); 318 bool MadeAnImprovement = true; 319 while (MadeAnImprovement) 320 { 321 MadeAnImprovement = false; 322 double MaxGain = 0.000001; // morally zero but must allow for rounding error 323 long index_from = 0; // index of PPPt we move from (initialize to keep compiler quiet) 324 long index_to = 0; // index of PPPt we move to (initialize to keep compiler quiet) 325 long IndexOrigPt = 0; // index of orig point we want to move (initialize to keep compiler quiet) 326 327 // For each PPPt i do ... 328 for (long i=0; i < NumPPP; ++i) 329 { 330 const long Ni = PPPts[i].myNumOrigPts; 331 if (Ni == 1) continue; 332 // For each index (of OrigPt associated to PPPt i) it do... 333 for (list<long>::iterator it = PPPts[i].myOrigPts.begin(); it != PPPts[i].myOrigPts.end(); ++it) 334 { 335 const double diffi = (Ni * PPPts[i].myDistanceSq[*it]) / (Ni-1); 336 // For each PPPt j (difft from PPPt i) do... 337 for (long j=0; j < NumPPP; ++j) 338 { 339 if (j == i) continue; 340 const long Nj = PPPts[j].myNumOrigPts; 341 const double diffj = -(Nj * PPPts[j].myDistanceSq[*it]) / (Nj+1); // careful here: Nj is unsigned! 342 const double gain = diffi + diffj; 343 if (gain > MaxGain) 344 { 345 MadeAnImprovement = true; 346 MaxGain = gain; 347 index_from = i; 348 index_to = j; 349 IndexOrigPt = *it; 350 } 351 } 352 } 353 } 354 355 if (MadeAnImprovement) 356 { 357 // Move orig point to new PPPt... 358 AddAndUpdate(PPPts[index_to], PPPts[index_to], IndexOrigPt, OrigPts); 359 EraseAndUpdate(PPPts[index_from], PPPts[index_from], IndexOrigPt, OrigPts); 360 } 361 } 362 } 363 364 365 // Initialize the list of preprocessed points for subdivision algm. PreprocessSubdivInit(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts)366 void PreprocessSubdivInit(vector<PPP>& PPPts, const vector<PointDbl>& OrigPts) 367 { 368 CoCoA_ASSERT(PPPts.empty()); 369 PPPts.resize(1); 370 const long NumPts = len(OrigPts); 371 for (long i=0; i < NumPts; ++i) 372 PPPts[0].myOrigPts.push_back(i); 373 PPPts[0].myNumOrigPts = NumPts; 374 PPPts[0].myCoords = SubsetAve(PPPts[0].myOrigPts, OrigPts); 375 PPPts[0].myDistanceSq.resize(NumPts); 376 ComputeDistSq(PPPts[0], OrigPts); 377 } 378 379 380 // Determine if some OrigPt is too far from its corresponding PPPt. 381 // If result is true, then WorstOrigIdx and WorstPPPIdx contain the indices 382 // of the most distant pair of orig and preprocessed points. ExistsBadPoint(long & WorstPPPIdx,const vector<PPP> & V)383 bool ExistsBadPoint(long& WorstPPPIdx, const vector<PPP>& V) 384 { 385 double WorstDist = 0; 386 const long NumPPP = len(V); 387 for (long i=0; i < NumPPP; ++i) 388 { 389 const long BadIndex = V[i].myWorstOrigPt; 390 if (V[i].myDistanceSq[BadIndex] > WorstDist) 391 { 392 WorstDist = V[i].myDistanceSq[BadIndex]; 393 WorstPPPIdx = i; 394 } 395 } 396 const long dim = len(V[0].myCoords); 397 const double CriticalRadiusSq = square(2*UnitVolBallRadius(dim)); // inefficient if dim is large and num of pts is small 398 return WorstDist >= CriticalRadiusSq; 399 } 400 401 402 // Adjoin a new preprocessed point onto the end of V. 403 // The new point comprises just one OrigPt, the one with index OrigPtIdx. NewPPP(vector<PPP> & PPPts,const PointDbl & P,long OrigPtIdx)404 void NewPPP(vector<PPP>& PPPts, const PointDbl& P, long OrigPtIdx) 405 { 406 const long NumPPPts = len(PPPts); 407 PPPts.resize(NumPPPts+1); 408 PPP& NewPt(PPPts[NumPPPts]); 409 NewPt.myCoords = P; 410 NewPt.myOrigPts.push_back(OrigPtIdx); 411 NewPt.myNumOrigPts = 1; 412 NewPt.myDistanceSq.push_back(0.0); 413 } 414 415 // Adjoin a new preprocessed point onto the end of V. 416 // The new point comprises just one OrigPt, the one with index OrigPtIdx. NewPPPSubdiv(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts,long OrigPtIdx)417 void NewPPPSubdiv(vector<PPP>& PPPts, 418 const vector<PointDbl>& OrigPts, 419 long OrigPtIdx) 420 { 421 const long NumPPPts = len(PPPts); 422 PointDbl P = OrigPts[OrigPtIdx]; 423 PPPts.resize(NumPPPts+1); 424 PPP& NewPt = PPPts[NumPPPts]; 425 NewPt.myCoords = P; 426 NewPt.myOrigPts.push_back(OrigPtIdx); 427 NewPt.myNumOrigPts = 1; 428 NewPt.myDistanceSq.resize(len(OrigPts)); 429 ComputeDistSq(NewPt, OrigPts); 430 NewPt.myWorstOrigPt = OrigPtIdx; 431 } 432 433 434 } // end of anonymous namespace 435 436 PreprocessPts(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)437 void PreprocessPts(std::vector<ApproxPts::PointR>& NewPts, 438 std::vector<long>& weights, 439 const std::vector<ApproxPts::PointR>& OrigPts, 440 std::vector<RingElem> tolerance) 441 { 442 // NOT EXCEPTION CLEAN!!! But does it matter in this case?? 443 PreprocessPtsGrid(NewPts, weights, OrigPts, tolerance); 444 // Crude heuristic for choosing between "aggr" and "subdiv"... needs improving!!! 445 if (len(NewPts) > len(OrigPts)/len(NewPts)) // equiv len(NewPts) > sqrt(len(OrigPts)) 446 PreprocessPtsAggr(NewPts, weights, OrigPts, tolerance); 447 else 448 PreprocessPtsSubdiv(NewPts, weights, OrigPts, tolerance); 449 } 450 451 PreprocessCheckArgs(const std::vector<ApproxPts::PointR> & OrigPts,const std::vector<RingElem> & tolerance,const char * const FnName)452 void PreprocessCheckArgs(const std::vector<ApproxPts::PointR>& OrigPts, 453 const std::vector<RingElem>& tolerance, 454 const char* const FnName) 455 { 456 // Some sanity checks on the inputs. 457 const long NumPts = len(OrigPts); 458 if (NumPts == 0) CoCoA_ERROR(ERR::Empty, FnName); 459 const long dim = len(OrigPts[0]); 460 if (dim == 0) CoCoA_ERROR(ERR::Empty, FnName); 461 if (len(tolerance) != dim) CoCoA_ERROR(ERR::IncompatDims, FnName); 462 463 // Check that all points lie in the same space over the same ring. 464 ring R = owner(OrigPts[0][0]); 465 if (!IsOrderedDomain(R)) CoCoA_ERROR(ERR::NotOrdDom, FnName); 466 for (long i=0; i < NumPts; ++i) 467 { 468 if (len(OrigPts[i]) != dim) CoCoA_ERROR(ERR::IncompatDims, FnName); 469 for (long j=0; j < dim; ++j) 470 if (owner(OrigPts[i][j]) != R) CoCoA_ERROR(ERR::MixedRings, FnName); 471 } 472 for (long j=0; j < dim; ++j) 473 { 474 if (owner(tolerance[j]) != R) CoCoA_ERROR(ERR::MixedRings, FnName); 475 if (tolerance[j] < 0) CoCoA_ERROR(ERR::NotNonNegative, FnName); 476 } 477 } 478 479 ///////////////////////////////////////////////////////////////////////////// 480 // Below are the three main algorithms. 481 // First the "grid" algorithm. 482 PreprocessPtsGrid(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)483 void PreprocessPtsGrid(std::vector<ApproxPts::PointR>& NewPts, 484 std::vector<long>& weights, 485 const std::vector<ApproxPts::PointR>& OrigPts, 486 std::vector<RingElem> tolerance) 487 { 488 PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsGrid"); 489 const long NumPts = len(OrigPts); 490 491 // Normalization effectively makes the tolerance in each direction equal to 1. 492 ZeroToleranceHack(OrigPts, tolerance); 493 vector<PointDbl> ScaledPts; // value is set by NormalizePts 494 NormalizePts(ScaledPts, OrigPts, tolerance); 495 496 vector<PPP> PPPts; PPPts.reserve(NumPts); 497 // For each OrigPts[i] do... 498 for (long i = 0; i < NumPts; ++i) 499 { 500 const PointDbl P = GridNearPoint(ScaledPts[i]); 501 const vector<PPP>::iterator pos = find_if(PPPts.begin(), PPPts.end(), CoordsEq(P)); 502 if (pos != PPPts.end()) 503 { 504 pos->myOrigPts.push_back(i); // adjoin to existing class 505 ++(pos->myNumOrigPts); // increment myNumOrigPts 506 } 507 else 508 NewPPP(PPPts, P, i); // add a new PPPt 509 } 510 511 const long NumPPPts = len(PPPts); 512 NewPts.resize(NumPPPts); 513 weights.resize(NumPPPts); 514 for (long k = 0; k < NumPPPts; ++k) 515 { 516 NewPts[k] = SubsetAve(PPPts[k].myOrigPts, OrigPts); 517 weights[k] = PPPts[k].myNumOrigPts; 518 } 519 } 520 521 522 ///////////////////////////////////////////////////////////////////////////// 523 // The "aggregative" algorithm. 524 PreprocessPtsAggr(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)525 void PreprocessPtsAggr(std::vector<ApproxPts::PointR>& NewPts, 526 std::vector<long>& weights, 527 const std::vector<ApproxPts::PointR>& OrigPts, 528 std::vector<RingElem> tolerance) 529 { 530 PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsAggr"); 531 const long NumPts = len(OrigPts); 532 const long dim = len(tolerance); 533 534 // Normalization effectively makes the tolerance in each direction equal to 1. 535 ZeroToleranceHack(OrigPts, tolerance); 536 vector<PointDbl> ScaledPts; // value set by NormalizePts 537 NormalizePts(ScaledPts, OrigPts, tolerance); 538 539 vector<PPP> PPPts(NumPts); 540 for (long i=0; i < NumPts; ++i) 541 { 542 PPPts[i].myCoords = ScaledPts[i]; 543 PPPts[i].myNumOrigPts = 1; 544 PPPts[i].myOrigPts.push_back(i); 545 } 546 547 // nbrs contains all pairs which could conceivably collapse together 548 // (i.e. only pairs separated by at most the CriticalRadius) 549 const double CriticalRadiusSq = square(2*UnitVolBallRadius(dim)); 550 list<NearPair> nbrs; 551 for (long i=0; i < NumPts; ++i) 552 { 553 for (long j=i+1; j < NumPts; ++j) 554 { 555 const double d2 = L2NormSq(PPPts[i].myCoords, PPPts[j].myCoords); 556 if (d2 < 4*CriticalRadiusSq) 557 nbrs.push_back(NearPair(i,j,d2)); 558 } 559 } 560 561 nbrs.sort(LessNearPair); 562 563 PointDbl CofG(dim); 564 while (!nbrs.empty() && nbrs.begin()->mySepSq < 4*CriticalRadiusSq) 565 { 566 const long i = nbrs.front().myLowerIndex; 567 const long j = nbrs.front().myHigherIndex; 568 nbrs.erase(nbrs.begin()); // delete first elem of nbrs 569 570 const PPP& Pi = PPPts[i]; 571 const PPP& Pj = PPPts[j]; 572 // Compute CofG of the union of Pi an Pj... 573 WeightedAve(CofG, len(Pi.myOrigPts), Pi.myCoords, len(Pj.myOrigPts), Pj.myCoords); 574 575 // Check that all original points in the union are close to CofG. 576 bool OK = true; 577 // for (list<long>::const_iterator it=Pi.myOrigPts.begin(); OK && it != Pi.myOrigPts.end(); ++it) 578 for (long k: Pi.myOrigPts) 579 { 580 OK &= (L2NormSq(CofG, ScaledPts[k]) < CriticalRadiusSq); 581 } 582 // for (list<long>::const_iterator it=Pj.myOrigPts.begin(); OK && it != Pj.myOrigPts.end(); ++it) 583 for (long k: Pj.myOrigPts) 584 { 585 OK &= (L2NormSq(CofG, ScaledPts[k]) < CriticalRadiusSq); 586 } 587 if (!OK) continue; // We cannot unite these two points, so go on to next pair of points. 588 589 // The union is fine, so accept it. We put all data into i-th PPP 590 // and empty the data of the j-th PPP. 591 { 592 list<long>& PPPOrigPts = PPPts[i].myOrigPts; 593 PPPOrigPts.splice(PPPOrigPts.begin(), PPPts[j].myOrigPts); 594 PPPts[i].myNumOrigPts += PPPts[j].myNumOrigPts; 595 PPPts[j].myNumOrigPts = 0; 596 } 597 nbrs.remove_if(NearPairContains(j)); // since Pj has been eliminated. 598 // Update all entries refering to i-th point. 599 for (list<NearPair>::iterator nbr = nbrs.begin(); nbr != nbrs.end(); ++nbr) 600 { 601 if (!NearPairContains(i)(*nbr)) continue; 602 long other = nbr->myLowerIndex; 603 if (other == i) other = nbr->myHigherIndex; 604 nbr->mySepSq = L2NormSq(CofG, PPPts[other].myCoords); 605 } 606 nbrs.sort(LessNearPair); // put the list back into order -- SLUG SLUG SLUG!!! 607 swap(PPPts[i].myCoords, CofG); // really an assignment to PPPts[i].myCoords 608 } 609 610 // There are no futher possible mergings, so copy answer into NewPts & weights. 611 NewPts.clear(); 612 weights.clear(); 613 for (long i=0; i < NumPts; ++i) 614 { 615 const long N = PPPts[i].myNumOrigPts; 616 if (N == 0) continue; 617 NewPts.push_back(SubsetAve(PPPts[i].myOrigPts, OrigPts)); 618 weights.push_back(N); 619 } 620 } 621 622 623 ///////////////////////////////////////////////////////////////////////////// 624 // The "subdivision" algorithm. 625 PreprocessPtsSubdiv(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)626 void PreprocessPtsSubdiv(std::vector<ApproxPts::PointR>& NewPts, 627 std::vector<long>& weights, 628 const std::vector<ApproxPts::PointR>& OrigPts, 629 std::vector<RingElem> tolerance) 630 { 631 PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsSubdiv"); 632 const long NumPts = len(OrigPts); 633 634 // Normalization effectively makes the tolerance in each direction equal to 1. 635 ZeroToleranceHack(OrigPts, tolerance); 636 vector<PointDbl> ScaledPts; // value is set by NormalizePts 637 NormalizePts(ScaledPts, OrigPts, tolerance); 638 639 // Initially all orig points are associated to a single preprocessed point. 640 vector<PPP> PPPts; PPPts.reserve(NumPts); 641 PreprocessSubdivInit(PPPts, ScaledPts); 642 643 // Main loop. 644 long WorstPPPIdx = 0; // Init value just to keep compiler quiet. 645 // True value is set in call to ExistsBadPoint (see next line) 646 while (ExistsBadPoint(WorstPPPIdx, PPPts)) 647 { 648 const long WorstOrigIdx = PPPts[WorstPPPIdx].myWorstOrigPt; 649 NewPPPSubdiv(PPPts, ScaledPts, WorstOrigIdx); 650 EraseAndUpdate(PPPts[WorstPPPIdx], PPPts[WorstPPPIdx], WorstOrigIdx, ScaledPts); 651 redistribute(PPPts, ScaledPts); 652 } 653 654 // Copy and denormalize result; add the weight of each PPP. 655 NewPts.clear(); NewPts.reserve(len(PPPts)); 656 weights.clear(); weights.reserve(len(PPPts)); 657 for (long i=0; i < len(PPPts); ++i) 658 { 659 const long N = PPPts[i].myNumOrigPts; 660 NewPts.push_back(SubsetAve(PPPts[i].myOrigPts, OrigPts)); 661 weights.push_back(N); 662 } 663 } 664 665 666 } // end of namespace CoCoA 667 668 669 // RCS header/log in the next few lines 670 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/ApproxPts.C,v 1.27 2020/02/18 11:25:12 abbott Exp $ 671 // $Log: ApproxPts.C,v $ 672 // Revision 1.27 2020/02/18 11:25:12 abbott 673 // Summary: Redmine 1346: new for loop syntax 674 // 675 // Revision 1.26 2017/02/15 12:23:33 abbott 676 // Summary: Added square fn (for double) 677 // 678 // Revision 1.25 2014/05/10 20:46:51 abbott 679 // Summary: Corrected arg checking; also now a separate fn 680 // Author: JAA 681 // 682 // Revision 1.24 2014/05/06 13:13:10 abbott 683 // Summary: Replaced assertions by "always" arg checks 684 // Author: JAA 685 // 686 // Revision 1.23 2014/04/30 16:03:53 abbott 687 // Summary: Eliminated use of sqrt (and corrected the program logic!) 688 // Author: JAA 689 // 690 // Revision 1.22 2013/03/27 18:24:33 abbott 691 // Added approx point preprocessing to C5; also changed names of the fns, and updated doc. 692 // 693 // Revision 1.21 2011/08/24 10:24:17 bigatti 694 // -- renamed QQ --> BigRat 695 // -- sorted #include 696 // 697 // Revision 1.20 2011/08/14 15:52:17 abbott 698 // Changed ZZ into BigInt (phase 1: just the library sources). 699 // 700 // Revision 1.19 2011/03/11 12:01:52 bigatti 701 // -- changed size --> len 702 // 703 // Revision 1.18 2011/03/11 11:08:27 bigatti 704 // -- changed size_t --> long 705 // -- changed size --> len 706 // 707 // Revision 1.17 2009/12/11 11:46:32 abbott 708 // Changed fn convert into IsConvertible. 709 // Added template procedure convert. 710 // New version because change is not backward compatible. 711 // 712 // Revision 1.16 2009/10/29 19:26:20 abbott 713 // Cleaned up include directives: now in alphabetical order, removed unneeded ones. 714 // 715 // Revision 1.15 2009/09/25 13:12:38 abbott 716 // Added initial value for WorstPPPIdx just to keep compiler quiet. 717 // 718 // Revision 1.14 2009/07/02 16:33:59 abbott 719 // Switched to using new IsRational interface (using a QQ value). 720 // 721 // Revision 1.13 2008/11/24 17:12:14 abbott 722 // Final tidying: renamed an internal fn, removed useless includes. 723 // 724 // Revision 1.12 2008/11/23 18:58:32 abbott 725 // Major overhaul to preprocessing and SOI/NBM code. 726 // Split SOI/NBM off into a separate file. 727 // Preprocessing is now "rational" (but internally guided by C++ doubles). 728 // SOI/NBM now each have 3 similar interfaces: one purely rational, one for 729 // input which is represented as doubles, and one which converts the input 730 // to RingTwinFloat values and produces a result which is over some RingTwinFloat 731 // (the precision is increased automatically until an answer is obtained). 732 // 733 // Revision 1.11 2008/11/20 10:47:30 abbott 734 // Now the code actually computes the list of weights. 735 // 736 // Revision 1.10 2008/11/20 10:08:15 abbott 737 // The preprocessing fns now return also the weights associated with the representatives. 738 // 739 // Revision 1.9 2008/11/06 12:50:44 abbott 740 // Moved definitions of square and round to utils.H from ApproxPts.H 741 // 742 // Revision 1.8 2008/10/07 15:45:22 abbott 743 // Changed ErrorInfo objects so they include the name of their own error ID. 744 // Changed catch statements to catch const objects. 745 // Removed calls to the member fn which accessed the error ID member of an 746 // ErrorInfo; now you simply compare directly with the error ID (makes the 747 // code easier to read). 748 // 749 // Revision 1.7 2008/09/12 13:28:43 bigatti 750 // -- new: NBM implementation 751 // 752 // Revision 1.6 2008/06/04 18:27:37 abbott 753 // Modified the server interface for "SOI": it now accepts a 3rd arg (gamma). 754 // 755 // Revision 1.5 2008/05/30 14:20:43 abbott 756 // SOI now returns also the "almost vanishing" polynomials. 757 // 758 // Revision 1.4 2008/05/30 12:51:08 abbott 759 // Added a comment. 760 // 761 // Revision 1.3 2008/05/29 15:46:29 bigatti 762 // -- added Approximate Border Basis (by Abbott,Torrente) 763 // 764 // Revision 1.2 2007/10/30 17:14:08 abbott 765 // Changed licence from GPL-2 only to GPL-3 or later. 766 // New version for such an important change. 767 // 768 // Revision 1.1.1.1 2007/03/09 15:16:11 abbott 769 // Imported files 770 // 771 // Revision 1.8 2007/03/08 18:22:30 cocoa 772 // Just whitespace cleaning. 773 // 774 // Revision 1.7 2006/12/21 13:48:33 cocoa 775 // Made all increment/decrement calls prefix (except where the must be postfix). 776 // 777 // Revision 1.6 2006/11/22 14:43:32 cocoa 778 // -- minor cleaning (indicated by Intel compiler) 779 // 780 // Revision 1.5 2006/11/20 15:53:38 cocoa 781 // Minor cleaning. Improved comments. 782 // 783 // Revision 1.4 2006/10/31 14:26:03 cocoa 784 // Added some missing consts; should make the code a little easier to comprehend. 785 // 786 // Revision 1.3 2006/10/06 14:04:15 cocoa 787 // Corrected position of #ifndef in header files. 788 // Separated CoCoA_ASSERT into assert.H from config.H; 789 // many minor consequential changes (have to #include assert.H). 790 // A little tidying of #include directives (esp. in Max's code). 791 // 792 // Revision 1.2 2006/06/21 17:05:47 cocoa 793 // Major overhaul of approx point preprocessing algms. 794 // 795 // Revision 1.1.1.1 2006/05/30 11:39:37 cocoa 796 // Imported files 797 // 798 // Revision 1.3 2006/05/29 16:16:47 cocoa 799 // Added "disj" preprocessing algorithm. 800 // 801 // Revision 1.2 2006/05/22 15:52:16 cocoa 802 // Added preprocess-disg algorithm to ApproxPts. 803 // Sundry minor improvements. 804 // 805 // Revision 1.1 2006/05/12 13:16:30 cocoa 806 // Added functions for preprocessing approximate points. 807 // 808 // 809