CDT++
Causal Dynamical Triangulations in C++
Loading...
Searching...
No Matches
Metropolis.hpp
Go to the documentation of this file.
1/*******************************************************************************
2 Causal Dynamical Triangulations in C++ using CGAL
3
4 Copyright © 2015 Adam Getchell
5 ******************************************************************************/
6
16
17#ifndef INCLUDE_METROPOLIS_HPP_
18#define INCLUDE_METROPOLIS_HPP_
19
20// CDT headers
21#include "Move_command.hpp"
22#include "Move_strategy.hpp"
23#include "S3Action.hpp"
24
25using Gmpzf = CGAL::Gmpzf;
26
36template <typename ManifoldType>
37class MoveStrategy<Strategies::METROPOLIS, ManifoldType>
38{
40
42 long double m_Alpha{};
43
45 long double m_K{};
46
49 long double m_Lambda{};
50
54 Int_precision m_passes{1};
55
59 Int_precision m_checkpoint{1};
60
63
67
70
73
77
80
84
85 public:
87 MoveStrategy() = default;
88
97 [[maybe_unused]] MoveStrategy(long double const Alpha, long double const K,
98 long double const Lambda,
99 Int_precision const passes,
100 Int_precision const checkpoint)
101 : m_Alpha(Alpha)
102 , m_K(K)
103 , m_Lambda(Lambda)
104 , m_passes(passes)
105 , m_checkpoint{checkpoint}
106 {
107#ifndef NDEBUG
108 spdlog::debug("{} called.\n", __PRETTY_FUNCTION__);
109#endif
110 }
111
113 [[nodiscard]] auto Alpha() const noexcept { return m_Alpha; }
114
116 [[nodiscard]] auto K() const noexcept { return m_K; }
117
119 [[nodiscard]] auto Lambda() const noexcept { return m_Lambda; }
120
122 [[nodiscard]] auto passes() const noexcept { return m_passes; }
123
125 [[nodiscard]] auto checkpoint() const noexcept { return m_checkpoint; }
126
128 auto get_proposed() const { return m_proposed_moves; }
129
131 auto get_accepted() const { return m_accepted_moves; }
132
134 auto get_rejected() const { return m_rejected_moves; }
135
137 auto get_attempted() const { return m_attempted_moves; }
138
140 auto get_succeeded() const { return m_succeeded_moves; }
141
143 auto get_failed() const { return m_failed_moves; }
144
152 auto CalculateA1(move_tracker::move_type move) const noexcept
153 {
154 auto all_moves = m_proposed_moves.total();
155 auto this_move = m_proposed_moves[move];
156 // Set precision for initialization and assignment functions
157 mpfr_set_default_prec(PRECISION);
158
159 // Initialize for MPFR
160 mpfr_t r_1;
161 mpfr_t r_2;
162 mpfr_t a_1;
163 mpfr_inits2(PRECISION, a_1, nullptr); // NOLINT
164
165 mpfr_init_set_si(r_1, this_move, MPFR_RNDD); // r_1 = this_move NOLINT
166 mpfr_init_set_si(r_2, all_moves, MPFR_RNDD); // r_2 = total_moves NOLINT
167
168 // The result
169 mpfr_div(a_1, r_1, r_2, MPFR_RNDD); // a_1 = r_1/r_2 NOLINT
170
171 // Convert mpfr_t total to Gmpzf result by using Gmpzf(double d)
172 auto result = mpfr_get_ld(a_1, MPFR_RNDD); // NOLINT
173
174 // Free memory
175 mpfr_clears(r_1, r_2, a_1, nullptr); // NOLINT
176
177#ifndef NDEBUG
178 spdlog::debug("{} called.\n", __PRETTY_FUNCTION__);
179 spdlog::trace("Total proposed moves = {}\n", all_moves);
180 spdlog::trace("A1 is {}\n", result);
181#endif
182
183 return result;
184 } // CalculateA1()
185
191 template <int dimension>
192 auto CalculateA2(move_tracker::move_type const move) const noexcept
193 {
194 if (dimension == 3)
195 {
196 auto currentS3Action =
197 S3_bulk_action(m_geometry.N1_TL, m_geometry.N3_31_13,
198 m_geometry.N3_22, m_Alpha, m_K, m_Lambda);
199 auto newS3Action = static_cast<Gmpzf>(0);
200 switch (move)
201 {
202 case move_tracker::move_type::TWO_THREE:
203 // A (2,3) move adds a timelike edge
204 // and a (2,2) simplex
205 newS3Action =
206 S3_bulk_action(m_geometry.N1_TL + 1, m_geometry.N3_31_13,
207 m_geometry.N3_22 + 1, m_Alpha, m_K, m_Lambda);
208 break;
209 case move_tracker::move_type::THREE_TWO:
210 // A (3,2) move removes a timelike edge
211 // and a (2,2) simplex
212 newS3Action =
213 S3_bulk_action(m_geometry.N1_TL - 1, m_geometry.N3_31_13,
214 m_geometry.N3_22 - 1, m_Alpha, m_K, m_Lambda);
215 break;
216 case move_tracker::move_type::TWO_SIX:
217 // A (2,6) move adds 2 timelike edges and
218 // 2 (1,3) and 2 (3,1) simplices
219 newS3Action =
220 S3_bulk_action(m_geometry.N1_TL + 2, m_geometry.N3_31_13 + 4,
221 m_geometry.N3_22, m_Alpha, m_K, m_Lambda);
222 break;
223 case move_tracker::move_type::SIX_TWO:
224 // A (6,2) move removes 2 timelike edges and
225 // 2 (1,3) and 2 (3,1) simplices
226 newS3Action =
227 S3_bulk_action(m_geometry.N1_TL - 2, m_geometry.N3_31_13,
228 m_geometry.N3_22 - 4, m_Alpha, m_K, m_Lambda);
229 break;
230 case move_tracker::move_type::FOUR_FOUR:
231 // A (4,4) move changes nothing with respect to the action,
232 // and e^0==1
233#ifndef NDEBUG
234 spdlog::trace("A2 is 1\n");
235#endif
236 return static_cast<long double>(1);
237 default: break;
238 }
239
240 auto exponent = currentS3Action - newS3Action;
241 auto exponent_double = utilities::Gmpzf_to_double(exponent);
242
243 // if exponent > 0 then e^exponent >=1 so according to Metropolis
244 // algorithm return A2=1
245 if (exponent >= 0) { return static_cast<long double>(1); }
246
247 // Set precision for initialization and assignment functions
248 mpfr_set_default_prec(PRECISION);
249
250 // Initialize for MPFR
251 mpfr_t r_1;
252 mpfr_t a_2;
253 mpfr_inits2(PRECISION, a_2, nullptr); // NOLINT
254
255 // Set input parameters and constants to mpfr_t equivalents
256 mpfr_init_set_ld(r_1, exponent_double, // NOLINT
257 MPFR_RNDD); // r1 = exponent
258
259 // e^exponent
260 mpfr_exp(a_2, r_1, MPFR_RNDD); // NOLINT
261
262 // Convert mpfr_t total to Gmpzf result by using Gmpzf(double d)
263 auto result = mpfr_get_ld(a_2, MPFR_RNDD); // NOLINT
264
265 // Free memory
266 mpfr_clears(r_1, a_2, nullptr); // NOLINT
267
268#ifndef NDEBUG
269 spdlog::trace("A2 is {}\n", result);
270#endif
271
272 return result;
273 }
274 } // CalculateA2()
275
282 auto try_move(move_tracker::move_type const move) -> bool
283 {
284 // Record the proposed move
285 ++m_proposed_moves[as_integer(move)];
286
287 // Calculate probability
288 auto a_1 = CalculateA1(move);
289
290 // Make move if random number < probability
291 auto a_2 = CalculateA2<3>(move);
292
293 if (auto const trial_value = utilities::generate_probability();
294 trial_value <= a_1 * a_2)
295 {
296#ifndef NDEBUG
297 spdlog::debug("{} called.\n", __PRETTY_FUNCTION__);
298 spdlog::trace("Trying move.\n");
299 spdlog::trace("Move type = {}\n", as_integer(move));
300 spdlog::trace("Trial_value = {}\n", trial_value);
301 spdlog::trace("A1 = {}\n", a_1);
302 spdlog::trace("A2 = {}\n", a_2);
303 spdlog::trace("A1*A2 = {}\n", a_1 * a_2);
304 spdlog::trace("{}\n", trial_value <= a_1 * a_2 ? "Move accepted."
305 : "Move rejected.");
306#endif
307 // Accept the move
308 ++m_accepted_moves[as_integer(move)];
309 return true;
310 }
311
312 // Reject the move
313 ++m_rejected_moves[as_integer(move)];
314 return false;
315
316 } // try_move()
317
324 [[nodiscard]] auto initialize(ManifoldType t_manifold)
325 -> std::optional<MoveCommand<ManifoldType>>
326 try
327 {
328 MoveCommand command(t_manifold);
329 fmt::print("Making initial moves ...\n");
330
331 // Make a move of each type
332 for (auto move = 0;
333 move < move_tracker::moves_per_dimension(ManifoldType::dimension);
334 ++move)
335 {
336#ifndef NDEBUG
337 spdlog::trace("Making move {} ...\n", move);
338#endif
339 command.enqueue(move_tracker::as_move(move));
340 ++m_proposed_moves[move];
341 ++m_accepted_moves[move];
342 }
343
344 // Execute the initial moves
345 command.execute();
346 command.print_successful();
347 command.print_errors();
348
349 // Update attempted, succeeded, and failed moves from MoveCommand
350 m_attempted_moves += command.get_attempted();
351 m_succeeded_moves += command.get_succeeded();
352 m_failed_moves += command.get_failed();
353
354 // Reset the move counters
355 command.reset_counters();
356
357 // print initial results
358 auto initial_results = command.get_results();
359 initial_results.print();
360 initial_results.print_details();
361
362 return command;
363 }
364 catch (std::system_error const& SystemError)
365 {
366 spdlog::debug("Metropolis initialization failed with {} ... exiting.\n",
367 SystemError.what());
368 spdlog::trace("{}\n", SystemError.code().message());
369 return std::nullopt;
370 }
371
379 auto operator()(ManifoldType const& t_manifold) -> ManifoldType
380 {
381#ifndef NDEBUG
382 spdlog::debug("{} called.\n", __PRETTY_FUNCTION__);
383#endif
384
385 fmt::print(
386 "Starting Metropolis-Hastings algorithm in {}+1 dimensions ...\n",
387 ManifoldType::dimension - 1);
388
389 auto command = initialize(t_manifold).value_or(MoveCommand(t_manifold));
390
391 fmt::print("Making random moves ...\n");
392
393 // Loop through m_passes
394 for (auto pass_number = 1; pass_number <= m_passes; ++pass_number)
395 {
396 fmt::print("=== Pass {} ===\n", pass_number);
397 auto total_simplices_this_pass = command.get_const_results().N3();
398 // Attempt a random move per simplex
399 for (auto move_attempt = 0; move_attempt < total_simplices_this_pass;
400 ++move_attempt)
401 {
402 // Pick a move to attempt
403
404 if (auto move = move_tracker::generate_random_move_3(); try_move(move))
405 {
406 command.enqueue(move);
407 }
408 } // Ends loop through CurrentTotalSimplices
409
410 // Do the moves
411 command.execute();
412
413 // Update attempted and failed moves
414 this->m_attempted_moves += command.get_attempted();
415 this->m_succeeded_moves += command.get_succeeded();
416 this->m_failed_moves += command.get_failed();
417
418 // Do stuff on checkpoint
419 if (pass_number % m_checkpoint == 0)
420 {
421 fmt::print("=== Pass {} ===\n", pass_number);
422 fmt::print("Writing to file.\n");
423 print_results();
424 auto result = command.get_results();
425 utilities::write_file(result);
426 }
427 } // Ends loop through m_passes
428
429 // output results
430 fmt::print("=== Run results ===\n");
431 print_results();
432 return command.get_results();
433 } // operator()
434
437 {
438 if (ManifoldType::dimension == 3)
439 {
440 fmt::print("=== Move Results ===\n");
441 fmt::print(
442 "There were {} proposed moves with {} accepted moves and {} rejected "
443 "moves.\n",
444 m_proposed_moves.total(), m_accepted_moves.total(),
445 m_rejected_moves.total());
446 fmt::print(
447 "There were {} attempted moves with {} successful moves and {} "
448 "failed moves.\n",
449 m_attempted_moves.total(), m_succeeded_moves.total(),
450 m_failed_moves.total());
451 fmt::print(
452 "(2,3) moves: {} proposed ({} accepted and {} rejected) with {} "
453 "attempted ({} successful and {} failed).\n",
454 m_proposed_moves.two_three_moves(),
455 m_accepted_moves.two_three_moves(),
456 m_rejected_moves.two_three_moves(),
457 m_attempted_moves.two_three_moves(),
458 m_succeeded_moves.two_three_moves(),
459 m_failed_moves.two_three_moves());
460
461 fmt::print(
462 "(3,2) moves: {} proposed ({} accepted and {} rejected) with {} "
463 "attempted ({} successful and {} failed).\n",
464 m_proposed_moves.three_two_moves(),
465 m_accepted_moves.three_two_moves(),
466 m_rejected_moves.three_two_moves(),
467 m_attempted_moves.three_two_moves(),
468 m_succeeded_moves.three_two_moves(),
469 m_failed_moves.three_two_moves());
470
471 fmt::print(
472 "(2,6) moves: {} proposed ({} accepted and {} rejected) with {} "
473 "attempted ({} successful and {} failed).\n",
474 m_proposed_moves.two_six_moves(), m_accepted_moves.two_six_moves(),
475 m_rejected_moves.two_six_moves(), m_attempted_moves.two_six_moves(),
476 m_succeeded_moves.two_six_moves(), m_failed_moves.two_six_moves());
477
478 fmt::print(
479 "(6,2) moves: {} proposed ({} accepted and {} rejected) with {} "
480 "attempted ({} successful and {} failed).\n",
481 m_proposed_moves.six_two_moves(), m_accepted_moves.six_two_moves(),
482 m_rejected_moves.six_two_moves(), m_attempted_moves.six_two_moves(),
483 m_succeeded_moves.six_two_moves(), m_failed_moves.six_two_moves());
484
485 fmt::print(
486 "(4,4) moves: {} proposed ({} accepted and {} rejected) with {} "
487 "attempted ({} successful and {} failed).\n",
488 m_proposed_moves.four_four_moves(),
489 m_accepted_moves.four_four_moves(),
490 m_rejected_moves.four_four_moves(),
491 m_attempted_moves.four_four_moves(),
492 m_succeeded_moves.four_four_moves(),
493 m_failed_moves.four_four_moves());
494 }
495 } // print_results
496}; // Metropolis
497
498using Metropolis_3 =
500using Metropolis_4 =
502
503#endif // INCLUDE_METROPOLIS_HPP_
Do ergodic moves using the Command pattern.
Template class for move algorithms (strategies) on manifolds.
Strategies
The algorithms available to make ergodic moves on triangulations.
move_type
The types of 3D ergodic moves.
Calculate S3 bulk actions on 3D Delaunay Triangulations.
auto S3_bulk_action(Int_precision const N1_TL, Int_precision const N3_31_13, Int_precision const N3_22, long double const Alpha, long double const K, long double const Lambda) noexcept -> Gmpzf
Calculates the generalized S3 bulk action in terms of , , , , , and .
Definition: S3Action.hpp:252
std::int_fast32_t Int_precision
Definition: Settings.hpp:31
static Int_precision constexpr PRECISION
Sets the precision for MPFR.
Definition: Settings.hpp:44
void execute()
Execute all moves in the queue on the manifold.
void reset_counters()
Reset counters.
auto get_attempted() const -> Counter const &
Attempted moves by MoveCommand.
auto get_results() -> ManifoldType &
Results of the moves invoked by MoveCommand.
void print_errors() const
Print move errors.
auto get_failed() const -> Counter const &
Failed moves by MoveCommand.
auto get_succeeded() const
Successful moves by MoveCommand.
void print_successful() const
Print successful moves.
void enqueue(move_tracker::move_type const t_move)
Push a Pachner move onto the move queue.
Counter m_attempted_moves
The number of moves that were attempted by a MoveCommand.
Definition: Metropolis.hpp:76
Counter m_failed_moves
The number of moves that a MoveCommand failed to make due to an error.
Definition: Metropolis.hpp:83
auto CalculateA2(move_tracker::move_type const move) const noexcept
Calculate A2.
Definition: Metropolis.hpp:192
Counter m_succeeded_moves
The number of moves that succeeded in the MoveCommand.
Definition: Metropolis.hpp:79
Geometry< ManifoldType::dimension > m_geometry
The current geometry of the manifold.
Definition: Metropolis.hpp:62
auto operator()(ManifoldType const &t_manifold) -> ManifoldType
Call operator.
Definition: Metropolis.hpp:379
Counter m_proposed_moves
The number of moves the algorithm tried.
Definition: Metropolis.hpp:66
MoveStrategy(long double const Alpha, long double const K, long double const Lambda, Int_precision const passes, Int_precision const checkpoint)
Metropolis function object constructor.
Definition: Metropolis.hpp:97
Counter m_accepted_moves
The number of moves accepted by the algorithm.
Definition: Metropolis.hpp:69
auto try_move(move_tracker::move_type const move) -> bool
Try a move of the selected type.
Definition: Metropolis.hpp:282
auto CalculateA1(move_tracker::move_type move) const noexcept
Calculate A1.
Definition: Metropolis.hpp:152
auto initialize(ManifoldType t_manifold) -> std::optional< MoveCommand< ManifoldType > > try
Initialize the Metropolis algorithm.
Definition: Metropolis.hpp:324
Counter m_rejected_moves
The number of moves rejected by the algorithm.
Definition: Metropolis.hpp:72
Select a move algorithm.
The data and methods to track ergodic moves.
auto two_six_moves() -> auto &
Write access to (2,6) moves.
auto total() const noexcept
Total moves.
auto four_four_moves() -> auto &
Write access to (4,4) moves.
auto six_two_moves() -> auto &
Write access to (6,2) moves.
auto three_two_moves() -> auto &
Write access to (3,2) moves.
auto two_three_moves() -> auto &
Write access to (2,3) moves.