SemiDiscreteOT 1.0
Semi-Discrete Optimal Transport Library
Loading...
Searching...
No Matches
Lloyd.cc
Go to the documentation of this file.
2
3namespace fs = std::filesystem;
4
5using namespace dealii;
6
7template <int dim, int spacedim>
8Lloyd<dim, spacedim>::Lloyd(const MPI_Comm &comm)
9 : SemiDiscreteOT<dim, spacedim>(comm)
10 , param_manager_lloyd(comm)
11{}
12
13template <int dim, int spacedim>
15 const double absolute_threshold,
16 const unsigned int max_iterations)
17{
18 Timer timer;
19 timer.start();
20
21 this->pcout << Color::yellow << Color::bold << "Starting Lloyd algorithm with " << this->target_points.size()
22 << " target points and " << this->source_density.size() << " source points" << Color::reset << std::endl;
23
24 double l2_norm = 0.0;
25
26 // Init barycenters
27 barycenters.resize(this->target_points.size());
28 for (size_t i = 0; i < barycenters.size(); ++i) {
29 barycenters[i] = this->target_points[i];
30 }
31 barycenters_prev.resize(this->target_points.size());
32 for (size_t i = 0; i < barycenters_prev.size(); ++i) {
33 barycenters_prev[i] = this->target_points[i];
34 }
35
36 // Alternate sot iterations with centroid updates until convergence or max_iterations
37 for (unsigned int n_iter = 0; n_iter < max_iterations; ++n_iter)
38 {
39 run_sot_iteration(n_iter);
40 run_centroid_iteration(n_iter);
41
42 compute_step_norm(barycenters, barycenters_prev, l2_norm);
43
44 // Update barycenters for next iteration
45 barycenters_prev = barycenters;
46
47 if (l2_norm < absolute_threshold) {
48 this->pcout << Color::green << Color::bold << "Lloyd algorithm converged in "
49 << n_iter + 1 << " iterations with L2 norm: " << l2_norm
50 << " < threshold: " << absolute_threshold << Color::reset << std::endl;
51 break;
52 } else {
53 this->pcout << Color::red << Color::bold << "Lloyd: "<< "Lloyd algorithm did not converge in "
54 << n_iter + 1 << " iterations with L2 norm: " << l2_norm
55 << " > threshold: " << absolute_threshold << Color::reset << std::endl;
56 this->sot_solver->setup_target(
57 barycenters, this->target_density);
58 }
59 this->pcout << std::endl;
60 }
61}
62
63template <int dim, int spacedim>
65 const std::vector<Point<spacedim>>& barycenters_next,
66 const std::vector<Point<spacedim>>& barycenters_prev,
67 double &l2_norm)
68{
69 Assert(barycenters_next.size() == barycenters_prev.size(),
70 ExcDimensionMismatch(barycenters_next.size(), barycenters_prev.size()));
71
72 l2_norm = 0.0;
73 // Sum up squared distances between corresponding points
74 for (size_t i = 0; i < barycenters_next.size(); ++i) {
75 l2_norm += barycenters_next[i].distance_square(barycenters_prev[i]);
76 }
77
78 // Compute root mean squared distance
79 l2_norm = std::sqrt(l2_norm / barycenters_next.size());
80}
81
82template <int dim, int spacedim>
84 const unsigned int n_iter)
85{
86 this->pcout << std::endl << Color::yellow << Color::bold << "Starting weighted barycenter iteration " << n_iter+1 << " with " << barycenters.size()
87 << " barycenters" << Color::reset << std::endl;
88
89 Timer timer;
90 timer.start();
91
92 // Configure solver parameters
93 SotParameterManager::SolverParameters& solver_config = this->solver_params;
94
95 try
96 {
97
98 if (solver_config.use_epsilon_scaling && this->epsilon_scaling_handler) {
99 this->pcout << "Using epsilon scaling with EpsilonScalingHandler:" << std::endl
100 << " Initial epsilon: " << solver_config.epsilon << std::endl
101 << " Scaling factor: " << solver_config.epsilon_scaling_factor << std::endl
102 << " Number of steps: " << solver_config.epsilon_scaling_steps << std::endl;
103 // Compute epsilon distribution for a single level
104 std::vector<std::vector<double>> epsilon_distribution =
105 this->epsilon_scaling_handler->compute_epsilon_distribution(1);
106
107 if (!epsilon_distribution.empty() && !epsilon_distribution[0].empty()) {
108 const auto& epsilon_sequence = epsilon_distribution[0];
109
110 // Run optimization for each epsilon value
111 for (size_t i = 0; i < epsilon_sequence.size(); ++i) {
112 this->pcout << "\nEpsilon scaling step " << i + 1 << "/" << epsilon_sequence.size()
113 << " (ε = " << epsilon_sequence[i] << ")" << std::endl;
114
115 solver_config.epsilon = epsilon_sequence[i];
116
117 try {
118 this->sot_solver->evaluate_weighted_barycenters(
119 potential, barycenters, solver_config);
120
121 } catch (const SolverControl::NoConvergence& exc) {
122 if (exc.last_step >= this->solver_params.max_iterations) {
123 this->pcout << Color::red << Color::bold << " Warning: Barycenter evaluation failed at step " << i + 1
124 << " (epsilon=" << epsilon_sequence[i] << "): Max iterations reached"
125 << Color::reset << std::endl;
126 }
127 }
128 }
129 }
130 } else {
131 // Run single Barycenter evaluation with original epsilon
132 try {
133 this->sot_solver->evaluate_weighted_barycenters(
134 potential, barycenters, solver_config);
135 } catch (const SolverControl::NoConvergence& exc) {
136 this->pcout << Color::red << Color::bold << "Warning: Barycenter evaluation did not converge." << Color::reset << std::endl;
137 }
138 }
139 } catch (const std::exception &e){
140 this->pcout << Color::red << Color::bold << "An exception occurred during barycenter evaluation: " << e.what() << Color::reset << std::endl;
141 }
142
143 // Save barycenters to text file
144 // Only process with rank 0 should write the file
145 int rank;
146 MPI_Comm_rank(this->mpi_communicator, &rank);
147 if (rank == 0) {
148 std::string filename = "barycenters_" + std::to_string(n_iter) + ".ply";
149 std::ofstream out(filename);
150 if (out.is_open()) {
151 // Write PLY header
152 out << "ply\n";
153 out << "format ascii 1.0\n";
154 out << "element vertex " << barycenters.size() << "\n";
155 for (unsigned int d = 0; d < spacedim; ++d) {
156 out << "property float " << (d == 0 ? 'x' : (d == 1 ? 'y' : 'z')) << "\n";
157 }
158 out << "end_header\n";
159
160 // Write vertex data
161 for (const auto& point : barycenters) {
162 for (unsigned int d = 0; d < spacedim; ++d) {
163 out << point[d] << " ";
164 }
165 out << "\n";
166 }
167 out.close();
168 this->pcout << "Barycenters saved to " << filename << std::endl << std::endl;
169 } else {
170 this->pcout << Color::red << " Failed to save barycenters to " << filename << Color::reset << std::endl;
171 }
172 }
173
174 timer.stop();
175 this->pcout << Color::green << Color::bold << "Lloyd: "<< n_iter << " Barycenter iteration completed in " << timer.wall_time() << " seconds" << Color::reset << std::endl;
176}
177
178// run single sot optimization with epsilon scaling
179template <int dim, int spacedim>
181 const unsigned int n_iter)
182{
183 this->pcout << Color::yellow << Color::bold << "Starting SOT iteration " << n_iter+1 << " with " << this->target_points.size()
184 << " target points and " << this->source_density.size() << " source points" << Color::reset << std::endl;
185
186 // Source and target measures must be set
187 Assert(this->sot_solver->source_measure.initialized,
188 ExcMessage("Source measure must be set before running SOT iteration"));
189 Assert(this->sot_solver->target_measure.initialized,
190 ExcMessage("Target points must be set before running SOT iteration"));
191
192 Timer timer;
193 timer.start();
194
195 // Configure solver parameters
196 SotParameterManager::SolverParameters& solver_config = this->solver_params;
197
198 potential.reinit(this->target_points.size());
199 try
200 {
201 if (solver_config.use_epsilon_scaling && this->epsilon_scaling_handler)
202 {
203 this->pcout << "Using epsilon scaling with EpsilonScalingHandler:" << std::endl
204 << " Initial epsilon: " << solver_config.epsilon << std::endl
205 << " Scaling factor: " << solver_config.epsilon_scaling_factor << std::endl
206 << " Number of steps: " << solver_config.epsilon_scaling_steps << std::endl;
207 // Compute epsilon distribution for a single level
208 std::vector<std::vector<double>> epsilon_distribution =
209 this->epsilon_scaling_handler->compute_epsilon_distribution(1);
210
211 if (!epsilon_distribution.empty() && !epsilon_distribution[0].empty())
212 {
213 const auto &epsilon_sequence = epsilon_distribution[0];
214
215 // Run optimization for each epsilon value
216 for (size_t i = 0; i < epsilon_sequence.size(); ++i)
217 {
218 this->pcout << "\nEpsilon scaling step " << i + 1 << "/" << epsilon_sequence.size()
219 << " (ε = " << epsilon_sequence[i] << ")" << std::endl;
220
221 solver_config.epsilon = epsilon_sequence[i];
222
223 try
224 {
225 this->sot_solver->solve(potential, solver_config);
226
227 // Save intermediate results
228 if (i < epsilon_sequence.size() - 1)
229 {
230 std::string eps_suffix = "_eps" + std::to_string(i + 1);
231 this->save_results(potential, "potential" + eps_suffix);
232 }
233 }
234 catch (const SolverControl::NoConvergence &exc)
235 {
236 if (exc.last_step >= this->solver_params.max_iterations)
237 {
238 this->pcout << Color::red << Color::bold << " Warning: Optimization failed at step " << i + 1
239 << " (epsilon=" << epsilon_sequence[i] << "): Max iterations reached"
240 << Color::reset << std::endl;
241 }
242 }
243 }
244 }
245 }
246 else
247 {
248 // Run single optimization with original epsilon
249 try
250 {
251 this->sot_solver->solve(potential, solver_config);
252 }
253 catch (const SolverControl::NoConvergence &exc)
254 {
255 this->pcout << Color::red << Color::bold << "Warning: Optimization did not converge." << Color::reset << std::endl;
256 }
257 }
258 }
259 catch (const std::exception &e)
260 {
261 this->pcout << Color::red << Color::bold << "An exception occurred during SOT solve: " << e.what() << Color::reset << std::endl;
262 }
263
264 // Save final results
265 this->save_results(potential, "potentials");
266
267 timer.stop();
268 const double total_time = timer.wall_time();
269
270 // Save convergence info
271 if (Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0) {
272 std::string eps_dir = "output/epsilon_" + Utils::to_scientific_string(solver_config.epsilon);
273 fs::create_directories(eps_dir);
274 std::ofstream conv_info(eps_dir + "/convergence_info.txt");
275 conv_info << "Regularization parameter (ε): " << solver_config.epsilon << "\n";
276 conv_info << "Number of iterations: " << this->sot_solver->get_last_iteration_count() << "\n";
277 conv_info << "Final function value: " << this->sot_solver->get_last_functional_value() << "\n";
278 conv_info << "Last threshold value: " << this->sot_solver->get_last_distance_threshold() << "\n";
279 conv_info << "Total execution time: " << total_time << " seconds\n";
280 conv_info << "Convergence achieved: " << this->sot_solver->get_convergence_status() << "\n";
281 }
282}
283
284template <int dim, int spacedim>
286{
287 param_manager_lloyd.print_parameters();
288
289 if (this->solver_params.use_epsilon_scaling) {
290 this->epsilon_scaling_handler = std::make_unique<EpsilonScalingHandler>(
291 this->mpi_communicator,
292 this->solver_params.epsilon,
293 this->solver_params.epsilon_scaling_factor,
294 this->solver_params.epsilon_scaling_steps
295 );
296 }
297
298 if (this->selected_task == "lloyd")
299 {
300 run_lloyd();
301 }
302 else
303 {
304 this->pcout << "No valid task selected" << std::endl;
305 }
306}
307
308template class Lloyd<2>;
309template class Lloyd<3>;
310template class Lloyd<2, 3>;
A class for performing Lloyd's algorithm for semi-discrete optimal transport.
Definition Lloyd.h:24
void run_centroid_iteration(const unsigned int n_iter)
Runs a single iteration of the centroid computation.
Definition Lloyd.cc:83
Lloyd(const MPI_Comm &mpi_communicator)
Constructor for the Lloyd class.
Definition Lloyd.cc:8
void compute_step_norm(const std::vector< Point< spacedim > > &barycenters_next, const std::vector< Point< spacedim > > &barycenters_prev, double &l2_norm)
Computes the L2 norm of the step.
Definition Lloyd.cc:64
void run_sot_iteration(const unsigned int n_iter)
Runs a single iteration of the semi-discrete optimal transport solver.
Definition Lloyd.cc:180
void run_lloyd(const double absolute_threshold=1e-8, const unsigned int max_iterations=100)
Runs the Lloyd's algorithm with the given parameters.
Definition Lloyd.cc:14
void run()
Runs the Lloyd's algorithm.
Definition Lloyd.cc:285
Main class for the semi-discrete optimal transport solver.
const std::string reset
const std::string yellow
const std::string red
const std::string bold
const std::string green
std::string to_scientific_string(double value, int precision=6)
Convert a double to a string in scientific notation.
Definition utils.h:42
unsigned int epsilon_scaling_steps
Number of scaling steps.
bool use_epsilon_scaling
Enable epsilon scaling strategy.
double epsilon_scaling_factor
Factor for epsilon reduction.
double epsilon
Entropy regularization parameter.