Skip to content

Commit 0b058ad

Browse files
committed
[geogram] Update to 1.5.2
1 parent e1f7d56 commit 0b058ad

30 files changed

+711
-160
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ include(cmake/geogram.cmake)
3939

4040
set(VORPALINE_VERSION_MAJOR 1)
4141
set(VORPALINE_VERSION_MINOR 5)
42-
set(VORPALINE_VERSION_PATCH 1)
42+
set(VORPALINE_VERSION_PATCH 2)
4343
set(VORPALINE_VERSION ${VORPALINE_VERSION_MAJOR}.${VORPALINE_VERSION_MINOR}.${VORPALINE_VERSION_PATCH})
4444

4545
set(VORPALINE_INCLUDE_SUBPATH geogram${VORPALINE_VERSION_MAJOR})

src/lib/exploragram/optimal_transport/optimal_transport.cpp

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ namespace GEO {
134134
linesearch_init_iter_ = 0;
135135

136136
use_direct_solver_ = false;
137+
138+
nb_air_particles_ = 0;
139+
air_particles_ = nil;
140+
air_particles_stride_ = 0;
141+
air_fraction_ = 0.0;
142+
143+
clip_by_balls_ = false;
137144
}
138145

139146
OptimalTransportMap::~OptimalTransportMap() {
@@ -144,27 +151,45 @@ namespace GEO {
144151
void OptimalTransportMap::set_points(
145152
index_t nb_points, const double* points, index_t stride
146153
) {
154+
147155
if(stride == 0) {
148156
stride = dimension_;
149157
}
158+
159+
index_t nb_total = nb_points + nb_air_particles_;
150160

151161
// Note: we represent power diagrams as (d+1)-dim Voronoi diagrams.
152162
// The target points are lifted to (d+1)-dim.
153-
points_dimp1_.resize(nb_points * dimp1_);
163+
points_dimp1_.resize(nb_total * dimp1_);
154164
for(index_t i = 0; i < nb_points; ++i) {
165+
double* p = &points_dimp1_[i*dimp1_];
155166
for(index_t c=0; c<dimension_; ++c) {
156-
points_dimp1_[i*dimp1_+c] = points[i*stride+c];
167+
p[c] = points[i*stride+c];
157168
}
158-
points_dimp1_[i*dimp1_ + dimension()] = 0.0;
169+
p[dimension()] = 0.0; // Yes, dimension() and not dimension()-1
170+
// (for instance, in 2d, x->0, y->1, W->2)
159171
}
172+
for(index_t i=0; i<nb_air_particles_; ++i) {
173+
double* p = &points_dimp1_[(i + nb_points)*dimp1_];
174+
for(index_t c=0; c<dimension_; ++c) {
175+
p[c] = air_particles_[i*air_particles_stride_+c];
176+
}
177+
p[dimension()] = 0.0; // Yes, dimension() and not dimension()-1
178+
// (for instance, in 2d, x->0, y->1, W->2)
179+
}
160180
weights_.assign(nb_points, 0);
161-
constant_nu_ = total_mass_ / double(nb_points);
181+
constant_nu_ = (1.0 - air_fraction_) * total_mass_ / double(nb_points);
182+
if(air_fraction_ != 0.0 && nb_air_particles_ == 0) {
183+
// constant_nu_ = pi*R^2 -> R^2 = constant_nu_ / pi
184+
// TODO: volumetric case.
185+
weights_.assign(nb_points, constant_nu_ / M_PI);
186+
}
162187
}
163188

164189
void OptimalTransportMap::set_nu(index_t i, double nu) {
165190
geo_debug_assert(i < weights_.size());
166191
if(nu_.size() != weights_.size()) {
167-
nu_.assign(weights_.size(), 0.0);
192+
nu_.assign(weights_.size(), 0.0);
168193
}
169194
nu_[i] = nu;
170195
}
@@ -173,7 +198,7 @@ namespace GEO {
173198
index_t max_iterations, index_t n
174199
) {
175200
if(n == 0) {
176-
n = index_t(points_dimp1_.size() / dimp1_);
201+
n = index_t(points_dimp1_.size() / dimp1_) - nb_air_particles_;
177202
}
178203

179204
vector<double> pk(n);
@@ -212,13 +237,21 @@ namespace GEO {
212237
FOR(i,n) {
213238
epsilon0 = geo_min(epsilon0, nu(i));
214239
}
240+
if(nb_air_particles_ != 0.0) {
241+
epsilon0 = geo_min(epsilon0, air_fraction_ * total_mass_);
242+
}
215243
epsilon0 = 0.5 * epsilon0;
216244
}
217245

218246
if(verbose_) {
219247
Logger::out("OTM") << " Solving linear system" << std::endl;
220248
}
221249

250+
if(nbZ_ != 0) {
251+
std::cerr << "There were empty cells !!!!!!" << std::endl;
252+
std::cerr << "FATAL error, exiting Newton" << std::endl;
253+
return;
254+
}
222255
solve_linear_system();
223256

224257
if(verbose_) {
@@ -269,8 +302,10 @@ namespace GEO {
269302

270303
// Condition to exit linesearch loop
271304
if(
272-
(measure_of_smallest_cell_ >= epsilon0) &&
273-
(g_norm_ <= (1.0 - 0.5*alphak) * gknorm)
305+
(measure_of_smallest_cell_ >= epsilon0) && (
306+
(air_fraction_ != 0.0) ||
307+
(g_norm_ <= (1.0 - 0.5*alphak) * gknorm)
308+
)
274309
) {
275310
// Condition for global convergence
276311
if(g_norm_ < gradient_threshold(n)) {
@@ -307,36 +342,36 @@ namespace GEO {
307342
}
308343

309344
void OptimalTransportMap::optimize(index_t max_iterations) {
310-
311-
index_t n = index_t(points_dimp1_.size() / dimp1_);
312-
345+
346+
index_t n = index_t(points_dimp1_.size() / dimp1_) - nb_air_particles_;
347+
313348
// Sanity check
314349
if(nu_.size() != 0) {
315-
double total_nu = 0.0;
316-
FOR(i,n) {
317-
total_nu += nu(i);
318-
}
319-
std::cerr << "total nu=" << total_nu << std::endl;
320-
std::cerr << "total mass=" << total_mass_ << std::endl;
321-
if(::fabs(total_nu - total_mass_)/total_mass_ > 0.01) {
322-
Logger::warn("OTM") << "Specified nu do not sum to domain measure"
323-
<< std::endl;
324-
Logger::warn("OTM") << "rescaling..."
325-
<< std::endl;
326-
}
327-
FOR(i,n) {
328-
set_nu(i, nu(i) * total_mass_ / total_nu);
329-
}
330-
331-
total_nu = 0.0;
332-
FOR(i,n) {
333-
total_nu += nu(i);
334-
}
335-
if(::fabs(total_nu - total_mass_)/total_mass_ > 0.01) {
336-
Logger::warn("OTM") << "Specified nu do not sum to domain measure"
337-
<< std::endl;
338-
return;
339-
}
350+
double total_nu = 0.0;
351+
FOR(i,n) {
352+
total_nu += nu(i);
353+
}
354+
std::cerr << "total nu=" << total_nu << std::endl;
355+
std::cerr << "total mass=" << total_mass_ << std::endl;
356+
if(::fabs(total_nu - total_mass_)/total_mass_ > 0.01) {
357+
Logger::warn("OTM") << "Specified nu do not sum to domain measure"
358+
<< std::endl;
359+
Logger::warn("OTM") << "rescaling..."
360+
<< std::endl;
361+
}
362+
FOR(i,n) {
363+
set_nu(i, nu(i) * total_mass_ / total_nu);
364+
}
365+
366+
total_nu = 0.0;
367+
FOR(i,n) {
368+
total_nu += nu(i);
369+
}
370+
if(::fabs(total_nu - total_mass_)/total_mass_ > 0.01) {
371+
Logger::warn("OTM") << "Specified nu do not sum to domain measure"
372+
<< std::endl;
373+
return;
374+
}
340375
}
341376

342377
if(newton_) {
@@ -556,10 +591,17 @@ namespace GEO {
556591
for(index_t p = 0; p < n; ++p) {
557592
W = geo_max(W, w[p]);
558593
}
559-
for(index_t p = 0; p < n; ++p) {
560-
points_dimp1_[dimp1_ * p + dimension_] = ::sqrt(W - w[p]);
561-
}
562-
594+
for(index_t p = 0; p < n; ++p) {
595+
// Yes, dimension_ and not dimension_ -1,
596+
// for instance in 2d, x->0, y->1, W->2
597+
points_dimp1_[dimp1_ * p + dimension_] = ::sqrt(W - w[p]);
598+
}
599+
if(nb_air_particles_ != 0) {
600+
for(index_t p = 0; p < nb_air_particles_; ++p) {
601+
points_dimp1_[dimp1_ * (n + p) + dimension_] = ::sqrt(W - 0.0);
602+
}
603+
}
604+
563605
// Step 2: compute function and gradient
564606
{
565607
Stopwatch* SW = nil;
@@ -569,7 +611,7 @@ namespace GEO {
569611
Logger::out("OTM") << "In power diagram..." << std::endl;
570612
}
571613
}
572-
delaunay_->set_vertices(n, points_dimp1_.data());
614+
delaunay_->set_vertices((n + nb_air_particles_), points_dimp1_.data());
573615
if(verbose_ && newton_) {
574616
delete SW;
575617
}
@@ -634,6 +676,17 @@ namespace GEO {
634676
++nb_empty_cells;
635677
}
636678
}
679+
if(air_fraction_ != 0.0) {
680+
double air_mass = total_mass_;
681+
for(index_t i=0; i<n; ++i) {
682+
air_mass -= g[i];
683+
}
684+
if(fabs(air_mass) < 1e-30) {
685+
++nb_empty_cells;
686+
}
687+
measure_of_smallest_cell_ =
688+
GEO::geo_min(measure_of_smallest_cell_, air_mass);
689+
}
637690
}
638691

639692
if(update_fg) {

src/lib/exploragram/optimal_transport/optimal_transport.h

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ namespace GEO {
118118

119119
/**
120120
* \brief Sets the points that define the target distribution.
121+
* \details If air particles are used, then set_air_particles() needs
122+
* to be called before set_points().
121123
* \param[in] nb_points number of points in the target distribution
122124
* \param[in] points an array of size nb_points * dimension() with the
123125
* coordinates of the Diracs centers in the target
@@ -129,6 +131,47 @@ namespace GEO {
129131
index_t nb_points, const double* points, index_t stride=0
130132
);
131133

134+
/**
135+
* \brief Sets the air particles that define the volume occupied by the
136+
* free space.
137+
* \details If air particles are used, then set_air_particles() needs
138+
* to be called before set_points().
139+
* \param[in] nb_air_particles number of air particles.
140+
* \param[in] air_particles a pointer to the array of doubles with the
141+
* coordinates of the air particles.
142+
* \param[in] stride number of doubles between two consecutive air
143+
* particles in the array, or 0 if tightly packed.
144+
* \param[in] air_fraction the fraction of the total mass occupied by air.
145+
*/
146+
void set_air_particles(
147+
index_t nb_air_particles, const double* air_particles, index_t stride,
148+
double air_fraction
149+
) {
150+
nb_air_particles_ = nb_air_particles;
151+
air_particles_ = air_particles;
152+
air_particles_stride_ = (stride == 0) ? dimension_ : stride;
153+
air_fraction_ = air_fraction;
154+
// "continuous air" mode (air fraction declared without any air
155+
// particle).
156+
clip_by_balls_ = (nb_air_particles == 0) && (air_fraction != 0.0);
157+
}
158+
159+
/**
160+
* \brief Gets the air fraction.
161+
* \return the air fraction previously specified by set_air_particles()
162+
*/
163+
double air_fraction() const {
164+
return air_fraction_;
165+
}
166+
167+
/**
168+
* \brief Gets the number of air particles.
169+
* \return the air fraction previously specified by set_air_particles()
170+
*/
171+
index_t nb_air_particles() const {
172+
return nb_air_particles_;
173+
}
174+
132175
/**
133176
* \brief Sets the desired mass at one of the Diracs.
134177
* \details If unspecified, then default value is total mass
@@ -436,6 +479,17 @@ namespace GEO {
436479
*/
437480
void solve_linear_system();
438481

482+
/**
483+
* \brief Sets the initial value of the weight associated
484+
* with one of the points.
485+
* \param[in] i index of the point, in 0..nb_points-1, where
486+
* np_points corresponds to the parameter of set_points.
487+
* \param[in] w the value of the weight.
488+
*/
489+
void set_initial_weight(index_t i, double w) {
490+
weights_[i] = w;
491+
}
492+
439493
protected:
440494

441495
/**
@@ -634,7 +688,7 @@ namespace GEO {
634688
}
635689
return result;
636690
}
637-
691+
638692
protected:
639693
OptimalTransportMap* OTM_;
640694
bool weighted_;
@@ -721,6 +775,29 @@ namespace GEO {
721775

722776
/** \brief if true, uses a direct solver (SUPERLU) */
723777
bool use_direct_solver_;
778+
779+
/** \brief if set, pointer to the air particles. */
780+
const double* air_particles_;
781+
782+
/** \brief if non-zero, number of air particles. */
783+
index_t nb_air_particles_;
784+
785+
/**
786+
* \brief Number of doubles between two consecutive
787+
* air particles in air_particles_.
788+
*/
789+
index_t air_particles_stride_;
790+
791+
/**
792+
* \brief The fraction of the total mass occupied by air.
793+
*/
794+
double air_fraction_;
795+
796+
/**
797+
* \brief Enabled if air fraction is specified without any
798+
* air particles.
799+
*/
800+
bool clip_by_balls_;
724801
};
725802

726803
}

0 commit comments

Comments
 (0)