Geogram  Version 1.9.1
A programming library of geometric algorithms
optimal_transport.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2000-2022 Inria
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * * Neither the name of the ALICE Project-Team nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Contact: Bruno Levy
30  *
31  * https://www.inria.fr/fr/bruno-levy
32  *
33  * Inria,
34  * Domaine de Voluceau,
35  * 78150 Le Chesnay - Rocquencourt
36  * FRANCE
37  *
38  */
39 
40 #ifndef H_EXPLORAGRAM_OPTIMAL_TRANSPORT_OPTIMAL_TRANSPORT_H
41 #define H_EXPLORAGRAM_OPTIMAL_TRANSPORT_OPTIMAL_TRANSPORT_H
42 
44 
45 struct NLMatrixStruct;
46 typedef NLMatrixStruct* NLMatrix;
47 
48 namespace GEO {
54  OT_PRECG, OT_SUPERLU, OT_CHOLMOD
55  };
56 }
57 
58 #ifndef GOMGEN
59 
60 #include <geogram/mesh/mesh.h>
61 #include <geogram/voronoi/RVD.h>
63 #include <geogram/NL/nl.h>
64 #include <geogram/NL/nl_matrix.h>
65 #include <geogram/third_party/HLBFGS/HLBFGS.h>
66 
72 namespace GEO {
73  class CentroidalVoronoiTesselation;
74 
75 
92  public:
100  index_t dimension,
101  Mesh* mesh,
102  const std::string& delaunay = "default",
103  bool BRIO = false
104  );
105 
110 
115  index_t dimension() const {
116  return dimension_;
117  }
118 
123  Mesh& mesh() {
124  return *mesh_;
125  }
126 
133  void set_Newton(bool x) {
134  newton_ = x;
135  }
136 
149  index_t nb_points, const double* points, index_t stride=0
150  );
151 
165  index_t nb_air_particles, const double* air_particles, index_t stride,
166  double air_fraction
167  ) {
168  nb_air_particles_ = nb_air_particles;
169  air_particles_ = air_particles;
170  air_particles_stride_ = (stride == 0) ? dimension_ : stride;
171  air_fraction_ = air_fraction;
172  // "continuous air" mode (air fraction declared without any air
173  // particle).
174  clip_by_balls_ = (nb_air_particles == 0) && (air_fraction != 0.0);
175  }
176 
181  double air_fraction() const {
182  return air_fraction_;
183  }
184 
190  return nb_air_particles_;
191  }
192 
201  void set_nu(index_t i, double nu);
202 
203 
209  void set_Laguerre_centroids(double* x) {
210  Laguerre_centroids_ = x;
211  }
212 
218  void set_epsilon(double eps) {
219  epsilon_ = eps;
220  }
221 
222 
228  void set_linsolve_epsilon(double eps) {
229  linsolve_epsilon_ = eps;
230  }
231 
238  linsolve_maxiter_ = maxiter;
239  }
240 
247  linesearch_maxiter_ = maxiter;
248  }
249 
259  linesearch_init_iter_ = init_iter;
260  }
261 
269  void set_regularization(double eps_reg) {
270  epsilon_regularization_ = eps_reg;
271  }
272 
273 
282  linear_solver_ = solver;
283  }
284 
291  void optimize(index_t max_iterations);
292 
298  void set_verbose(bool x) {
299  verbose_ = x;
300  }
301 
312  void optimize_full_Newton(index_t max_iterations, index_t n=0);
313 
324  void optimize_level(index_t b, index_t e, index_t max_iterations);
325 
337  const vector<index_t>& levels, index_t max_iterations
338  );
339 
345  index_t nb_points() const {
346  return weights_.size();
347  }
348 
355  const double* point_ptr(index_t i) const {
356  geo_debug_assert(i < nb_points());
357  return &(points_dimp1_[dimp1_ * i]);
358  }
359 
365  double weight(index_t i) const {
366  return weights_[i];
367  }
368 
374  void set_weight(index_t i, double val) {
375  weights_[i] = val;
376  }
377 
383  double potential(index_t i) const {
384  return points_dimp1_[dimp1_*i + dimension_];
385  }
386 
395  static void funcgrad_CB(
396  index_t n, double* x, double& f, double* g
397  );
398 
407  static void newiteration_CB(
408  index_t n, const double* x, double f, const double* g, double gnorm
409  );
410 
416  return RVD_;
417  }
418 
432  bool x,
433  bool show_RVD_seed = false,
434  bool last_iter_only = false
435  ) {
436  if(last_iter_only) {
437  save_RVD_iter_ = false;
438  save_RVD_last_iter_ = true;
439  } else {
440  save_RVD_iter_ = x;
441  }
442  show_RVD_seed_ = show_RVD_seed;
443  }
444 
449  virtual void get_RVD(Mesh& M) = 0;
450 
456  virtual void compute_Laguerre_centroids(double* centroids) = 0;
457 
463 
470  void new_linear_system(index_t n, double* x);
471 
477  void add_ij_coefficient(index_t i, index_t j, double a) {
478  if(!user_H_g_) {
479  nlAddIJCoefficient(i,j,a);
480  } else {
481  if(user_H_ != nullptr) {
482  geo_debug_assert(user_H_->type == NL_MATRIX_SPARSE_DYNAMIC);
483  nlSparseMatrixAdd((NLSparseMatrix*)user_H_, i, j, a);
484  }
485  }
486  }
487 
493  void add_i_right_hand_side(index_t i, double a) {
494  if(!user_H_g_) {
495  nlAddIRightHandSide(i,a);
496  }
497  }
498 
505 
513  void set_initial_weight(index_t i, double w) {
514  weights_[i] = w;
515  }
516 
521  double total_mass() const {
522  return total_mass_;
523  }
524 
536  const double* weights, NLMatrix Laplacian, double* measures
537  );
538 
539  protected:
540 
545  double nu(index_t p) const {
546  return nu_.size() == 0 ? constant_nu_ : nu_[p];
547  }
548 
549 
553  virtual void newiteration();
554 
562  void save_RVD(index_t id);
563 
571  void funcgrad(index_t n, double* w, double& f, double* g);
572 
577  virtual void call_callback_on_RVD() = 0;
578 
589  index_t n, const double* w,
590  double& f, double* g
591  );
592 
602  double gradient_threshold(index_t n) const {
603  return ::sqrt(double(n) * geo_sqr(epsilon_ * constant_nu_));
604  }
605 
606  public:
607 
612  class Callback {
613  public:
620  ) : OTM_(OTM),
621  Newton_step_(false),
622  eval_F_(false),
623  n_(0),
624  w_(nullptr),
625  g_(nullptr),
626  mg_(nullptr) {
627  weighted_ =
628  OTM->mesh().vertices.attributes().is_defined("weight");
629  }
630 
634  virtual ~Callback();
635 
643  void set_Laguerre_centroids(double* mg) {
644  mg_ = mg;
645  }
646 
652  bool has_Laguerre_centroids() const {
653  return (mg_ != nullptr);
654  }
655 
662  double* Laguerre_centroids() {
663  return mg_;
664  }
665 
671  void set_w(const double* w, index_t n) {
672  w_ = w;
673  n_ = n;
674  }
675 
683  void set_Newton_step(bool Newton) {
684  Newton_step_ = Newton;
685  }
686 
692  bool is_Newton_step() const {
693  return Newton_step_;
694  }
695 
702  funcval_.assign(nb, 0.0);
703  }
704 
709  void set_g(double* g) {
710  g_ = g;
711  }
712 
721  void set_eval_F(bool x) {
722  eval_F_ = x;
723  }
724 
730  double funcval() const {
731  double result = 0.0;
732  FOR(i,funcval_.size()) {
733  result += funcval_[i];
734  }
735  return result;
736  }
737 
738  protected:
739  OptimalTransportMap* OTM_;
740  bool weighted_;
741  bool Newton_step_;
742  bool eval_F_;
743  vector<double> funcval_;
744  index_t n_;
745  const double* w_;
746  double* g_;
747  double* mg_;
748  };
749 
750  protected:
751  static OptimalTransportMap* instance_;
752  index_t dimension_;
754  Mesh* mesh_;
755  Delaunay_var delaunay_;
757  vector<double> points_dimp1_;
758  vector<double> weights_;
759  double total_mass_;
760  double constant_nu_;
762  double epsilon_;
764  index_t current_call_iter_;
765 
766  Callback* callback_;
767 
768  std::string last_stats_;
769  bool pretty_log_;
770  index_t level_;
771 
772  bool save_RVD_iter_;
773  bool save_RVD_last_iter_;
774  bool show_RVD_seed_;
775  index_t current_iter_;
776  bool newton_;
777  bool verbose_;
778 
785 
790 
794  double g_norm_;
795 
800 
806 
809 
812 
815 
818 
821 
824 
826  const double* air_particles_;
827 
830 
836 
841 
847 
848 
853  bool user_H_g_;
854 
859  };
860 
861 }
862 
863 #endif
864 
865 #endif
Class and functions to compute restricted Voronoi diagrams and extract information from them.
#define geo_debug_assert(x)
Verifies that a condition is met.
Definition: assert.h:196
bool is_defined(const std::string &name) const
Tests whether an attribute is defined.
Definition: attributes.h:949
AttributesManager & attributes() const
Gets the attributes manager.
Definition: mesh.h:100
Represents a mesh.
Definition: mesh.h:2701
Base class for the callbacks executed for each intersection between a Laguerre cell and a simplex of ...
void set_nb_threads(index_t nb)
Specifies the number of threads.
bool has_Laguerre_centroids() const
Tests whether Laguerre centroids should be computed.
bool is_Newton_step() const
Tests whether the current step is a Newton step.
void set_g(double *g)
Specifies where the gradient should be stored.
void set_Newton_step(bool Newton)
Specifies whether current step is a Newton step.
Callback(OptimalTransportMap *OTM)
Callback constructor.
double * Laguerre_centroids()
Gets a pointer to the Laguerre centroids.
void set_eval_F(bool x)
Specifies whether the objective function should be evaluated.
virtual ~Callback()
Callback destructor.
void set_w(const double *w, index_t n)
Sets the weight vector.
double funcval() const
Gets the computed value of the objective function.
void set_Laguerre_centroids(double *mg)
Sets where centroids should be output.
Computes semi-discrete optimal transport maps.
double gradient_threshold(index_t n) const
Computes the stopping criterion of the solver.
OTLinearSolver linear_solver_
one of OT_PRECG, OT_SUPERLU, OT_CHOLMOD.
double nu(index_t p) const
Gets the mass of the Dirac associated with point p.
NLMatrix user_H_
User-defined Hessian matrix.
double air_fraction_
The fraction of the total mass occupied by air.
void set_save_RVD_iter(bool x, bool show_RVD_seed=false, bool last_iter_only=false)
Sets whether the restricted Voronoi diagram at each iteration should be saved.
double measure_of_smallest_cell_
Measure of the smallest Laguerre cell.
void optimize_levels(const vector< index_t > &levels, index_t max_iterations)
Multi-level optimization.
void save_RVD(index_t id)
Saves the RVD at each iteration if specified on command line (just for debugging/ explaining the algo...
bool w_did_not_change_
True if w did not change, thus there is no need to recompute the power diagram.
void set_linsolve_epsilon(double eps)
Sets the tolerance for linear solve.
static void funcgrad_CB(index_t n, double *x, double &f, double *g)
Callback for the numerical solver.
double * Laguerre_centroids_
If user-specified, then Laguerre centroids are output here.
void set_linear_solver(OTLinearSolver solver)
Specifies whether a direct solver should be used.
void set_epsilon(double eps)
Sets the maximum error.
void set_nu(index_t i, double nu)
Sets the desired mass at one of the Diracs.
index_t linsolve_maxiter_
maximum number of iterations for linear solve
double epsilon_regularization_
Add a regularization term to remove translational degree of freedom for the weights.
bool user_H_g_
True if class is just used by user to compute Hessian and gradient instead of doing full computation.
index_t nb_points() const
Gets the number of points.
void set_regularization(double eps_reg)
Sets the weight of the regularization term.
double epsilon_
Acceptable relative deviation for the measure of a cell.
index_t nb_air_particles() const
Gets the number of air particles.
virtual ~OptimalTransportMap()
OptimalTransportMap destructor.
index_t air_particles_stride_
Number of doubles between two consecutive air particles in air_particles_.
OptimalTransportMap(index_t dimension, Mesh *mesh, const std::string &delaunay="default", bool BRIO=false)
OptimalTransportMap constructor.
virtual void get_RVD(Mesh &M)=0
Computes a mesh with the restricted Voronoi diagram.
bool clip_by_balls_
Enabled if air fraction is specified without any air particles.
void set_linesearch_init_iter(index_t init_iter)
Sets the number of steplength reductions to be done at the first iteration.
void solve_linear_system()
Solves a linear system.
const double * air_particles_
if set, pointer to the air particles.
void set_initial_weight(index_t i, double w)
Sets the initial value of the weight associated with one of the points.
double constant_nu_
Value of one of the Diracs if cte.
void new_linear_system(index_t n, double *x)
Starts a new linear system.
double weight(index_t i) const
Gets weight of a point.
void set_air_particles(index_t nb_air_particles, const double *air_particles, index_t stride, double air_fraction)
Sets the air particles that define the volume occupied by the free space.
void compute_P1_Laplacian(const double *weights, NLMatrix Laplacian, double *measures)
Computes the P1 Laplacian of the Laguerre cells.
double potential(index_t i) const
Gets the d+1-th coordinate of the embedding for a point.
void set_linsolve_maxiter(index_t maxiter)
Sets the maximum number of iterations for linear solve.
void set_weight(index_t i, double val)
Sets a weight of a point.
vector< double > nu_
Value of all the Diracs.
index_t nb_air_particles_
if non-zero, number of air particles.
void set_verbose(bool x)
Enable/disable messages during optimization.
static void newiteration_CB(index_t n, const double *x, double f, const double *g, double gnorm)
Callback for the numerical solver.
index_t linesearch_maxiter_
maximum number of steplength divisions
void eval_func_grad_Hessian(index_t n, const double *w, double &f, double *g)
Computes the objective function, its gradient and its Hessian.
RestrictedVoronoiDiagram * RVD()
Gets the restricted Voronoi diagram.
double g_norm_
Norm of the gradient in last iteration.
virtual void call_callback_on_RVD()=0
Calls the callback for each intersection between a Laguerre cell and a simplex of the background mesh...
double air_fraction() const
Gets the air fraction.
virtual void newiteration()
Callback for the numerical solver.
void add_i_right_hand_side(index_t i, double a)
Adds a coefficient to the right hand side.
void optimize(index_t max_iterations)
Computes the weights that realize the optimal transport map between the source mesh and the target po...
double total_mass() const
Gets the total mass of the domain.
virtual void compute_Laguerre_centroids(double *centroids)=0
Computes the centroids of the Laguerre cells.
void set_Laguerre_centroids(double *x)
Specifies a user vector where the centroids of the Laguerre cells will be stored after computing tran...
void optimize_level(index_t b, index_t e, index_t max_iterations)
Optimizes one level of the multilevel algorithm.
void set_linesearch_maxiter(index_t maxiter)
Sets the maximum number of line search iterations.
void update_sparsity_pattern()
Updates the sparsity pattern of the Hessian right after a new Laguerre diagram was computed.
const double * point_ptr(index_t i) const
Gets a point.
Mesh & mesh()
Gets the mesh.
void set_points(index_t nb_points, const double *points, index_t stride=0)
Sets the points that define the target distribution.
void set_Newton(bool x)
Sets whether Newton algorithm should be used.
index_t nbZ_
Number of empty cells in last iteration.
void funcgrad(index_t n, double *w, double &f, double *g)
Computes the objective function and its gradient.
double linsolve_epsilon_
maximum value of
index_t dimension() const
Gets the dimension.
index_t linesearch_init_iter_
starting number of steplength divisions
void add_ij_coefficient(index_t i, index_t j, double a)
Adds a coefficient to the matrix of the system.
index_t dimp1_
dimension_ + 1
void optimize_full_Newton(index_t max_iterations, index_t n=0)
Computes the weights that realize the optimal transport map between the source mesh and the target po...
Computes a Restricted Voronoi Diagram (RVD).
Definition: RVD.h:89
Abstract interface for Delaunay.
#define EXPLORAGRAM_API
Linkage declaration for exploragram symbols.
Definition: defs.h:18
Included by all headers in exploragram.
The class that represents a mesh.
Global Vorpaline namespace.
Definition: basic.h:55
T geo_sqr(T x)
Gets the square value of a value.
Definition: numeric.h:301
OTLinearSolver
Specifies the linear solver to be used with OptimalTransport.
geo_index_t index_t
The type for storing and manipulating indices.
Definition: numeric.h:329
The public API of the OpenNL linear solver library. Click the "More..." link below for simple example...
NLAPI NLulong NLAPIENTRY nlAddIJCoefficient(NLuint i, NLuint j, NLdouble value)
Adds a coefficient to the current matrix.
struct NLMatrixStruct * NLMatrix
Opaque handle to a matrix.
Definition: nl.h:1496
NLAPI void NLAPIENTRY nlAddIRightHandSide(NLuint i, NLdouble value)
Adds a coefficient to a component of the right hand side of the equation.
Internal OpenNL functions to manipulate sparse matrices.
The base class for abstract matrices.
Definition: nl_matrix.h:80