CDT++
Causal Dynamical Triangulations in C++
Loading...
Searching...
No Matches
Move_command_test.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Causal Dynamical Triangulations in C++ using CGAL
3
4 Copyright © 2018 Adam Getchell
5 ******************************************************************************/
6
10
11#include "Move_command.hpp"
12
13#include <doctest/doctest.h>
14#include <fmt/ranges.h>
15
16using namespace std;
17using namespace manifolds;
18
19SCENARIO("MoveCommand special members" * doctest::test_suite("move_command"))
20{
21 spdlog::debug("MoveCommand special members.\n");
22 GIVEN("A MoveCommand.")
23 {
24 WHEN("It's properties are examined.")
25 {
26 THEN("It is no-throw destructible.")
27 {
28 REQUIRE(is_nothrow_destructible_v<MoveCommand<Manifold_3>>);
29 spdlog::debug("It is no-throw destructible.\n");
30 }
31 THEN("It is not default constructible.")
32 {
33 CHECK_FALSE(is_default_constructible_v<MoveCommand<Manifold_3>>);
34 }
35 THEN("It is copy constructible.")
36 {
37 REQUIRE(is_copy_constructible_v<MoveCommand<Manifold_3>>);
38 spdlog::debug("It is copy constructible.\n");
39 }
40 THEN("It is copy assignable.")
41 {
42 REQUIRE(is_copy_assignable_v<MoveCommand<Manifold_3>>);
43 spdlog::debug("It is copy assignable.\n");
44 }
45 THEN("It is no-throw move constructible.")
46 {
47 REQUIRE(is_nothrow_move_constructible_v<MoveCommand<Manifold_3>>);
48 spdlog::debug("Small function optimization supported.");
49 spdlog::debug("It is no-throw move constructible.\n");
50 }
51 THEN("It is no-throw move assignable.")
52 {
53 REQUIRE(is_nothrow_move_assignable_v<MoveCommand<Manifold_3>>);
54 spdlog::debug("It is no-throw move assignable.\n");
55 }
56 THEN("It is no-throw swappable")
57 {
58 REQUIRE(is_nothrow_swappable_v<MoveCommand<Manifold_3>>);
59 spdlog::debug("It is no-throw swappable.\n");
60 }
61 THEN("It is constructible from a Manifold.")
62 {
63 REQUIRE(is_constructible_v<MoveCommand<Manifold_3>, Manifold_3>);
64 spdlog::debug("It is constructible from a Manifold.\n");
65 }
66 }
67 }
68}
69
70SCENARIO("Invoking a move with a function pointer" *
71 doctest::test_suite("move_command"))
72{
73 spdlog::debug("Invoking a move with a function pointer.\n");
74 GIVEN("A valid manifold.")
75 {
76 auto constexpr desired_simplices = 640;
77 auto constexpr desired_timeslices = 4;
78 Manifold_3 manifold(desired_simplices, desired_timeslices);
79 REQUIRE(manifold.is_correct());
80 WHEN("A function pointer is constructed for a move.")
81 {
82 auto* const move23{ergodic_moves::do_23_move};
83 THEN("Running the function makes the move.")
84 {
85 auto result = move23(manifold);
86 result->update();
87 CHECK(ergodic_moves::check_move(manifold, result.value(),
88 move_tracker::move_type::TWO_THREE));
89 // Human verification
90 fmt::print("Manifold properties:\n");
91 manifold.print_details();
92 fmt::print("Moved manifold properties:\n");
93 result->print_details();
94 }
95 }
96 }
97}
98
99SCENARIO("Invoking a move with a lambda" * doctest::test_suite("move_command"))
100{
101 spdlog::debug("Invoking a move with a lambda.\n");
102 GIVEN("A valid manifold.")
103 {
104 auto constexpr desired_simplices = 640;
105 auto constexpr desired_timeslices = 4;
106 Manifold_3 manifold(desired_simplices, desired_timeslices);
107 REQUIRE(manifold.is_correct());
108 WHEN("A lambda is constructed for a move.")
109 {
110 auto const move23 = [](Manifold_3& manifold_3) {
111 return ergodic_moves::do_23_move(manifold_3).value();
112 };
113 THEN("Running the lambda makes the move.")
114 {
115 auto result = move23(manifold);
116 result.update();
117 CHECK(ergodic_moves::check_move(manifold, result,
118 move_tracker::move_type::TWO_THREE));
119 // Human verification
120 fmt::print("Manifold properties:\n");
121 manifold.print_details();
122 fmt::print("Moved manifold properties:\n");
123 result.print_details();
124 }
125 }
126 }
127}
128
129SCENARIO("Invoking a move with apply_move and a function pointer" *
130 doctest::test_suite("move_command"))
131{
132 spdlog::debug("Invoking a move with apply_move and a function pointer.\n");
133 GIVEN("A valid manifold.")
134 {
135 auto constexpr desired_simplices = 640;
136 auto constexpr desired_timeslices = 4;
137 Manifold_3 manifold(desired_simplices, desired_timeslices);
138 REQUIRE(manifold.is_correct());
139 WHEN("Apply_move is used for a move.")
140 {
141 auto* move = ergodic_moves::do_23_move;
142 THEN("Invoking apply_move() makes the move.")
143 {
144 auto result = apply_move(manifold, move);
145 result->update();
146 CHECK(ergodic_moves::check_move(manifold, result.value(),
147 move_tracker::move_type::TWO_THREE));
148 // Human verification
149 fmt::print("Manifold properties:\n");
150 manifold.print_details();
151 fmt::print("Moved manifold properties:\n");
152 result->print_details();
153 }
154 }
155 }
156}
157
158SCENARIO("MoveCommand initialization" * doctest::test_suite("move_command"))
159{
160 spdlog::debug("MoveCommand initialization.\n");
161 GIVEN("A valid manifold.")
162 {
163 auto constexpr desired_simplices = 640;
164 auto constexpr desired_timeslices = 4;
165 Manifold_3 manifold(desired_simplices, desired_timeslices);
166 REQUIRE(manifold.is_correct());
167 WHEN("A Command is constructed with a manifold.")
168 {
169 MoveCommand const command(manifold);
170 THEN("The original is still valid.")
171 {
172 REQUIRE(manifold.is_correct());
173 // Human verification
174 manifold.print_details();
175 }
176 THEN("It contains the manifold.")
177 {
178 CHECK_EQ(manifold.N3(), command.get_const_results().N3());
179 CHECK_EQ(manifold.N3_31(), command.get_const_results().N3_31());
180 CHECK_EQ(manifold.N3_22(), command.get_const_results().N3_22());
181 CHECK_EQ(manifold.N3_13(), command.get_const_results().N3_13());
182 CHECK_EQ(manifold.N3_31_13(), command.get_const_results().N3_31_13());
183 CHECK_EQ(manifold.N2(), command.get_const_results().N2());
184 CHECK_EQ(manifold.N1(), command.get_const_results().N1());
185 CHECK_EQ(manifold.N1_TL(), command.get_const_results().N1_TL());
186 CHECK_EQ(manifold.N1_SL(), command.get_const_results().N1_SL());
187 CHECK_EQ(manifold.N0(), command.get_const_results().N0());
188 CHECK_EQ(manifold.max_time(), command.get_const_results().max_time());
189 CHECK_EQ(manifold.min_time(), command.get_const_results().min_time());
190 // Human verification
191 fmt::print("Manifold properties:\n");
192 manifold.print_details();
193 manifold.print_volume_per_timeslice();
194 fmt::print("Command.get_const_results() properties:\n");
195 command.get_const_results().print_details();
196 command.get_const_results().print_volume_per_timeslice();
197 }
198 THEN("The two manifolds are distinct.")
199 {
200 auto* manifold_ptr = &manifold;
201 auto const* manifold2_ptr = &command.get_const_results();
202 CHECK_NE(manifold_ptr, manifold2_ptr);
203 }
204 THEN("Attempted, succeeded, and failed moves are initialized to 0.")
205 {
206 CHECK_EQ(command.get_attempted().total(), 0);
207 CHECK_EQ(command.get_succeeded().total(), 0);
208 CHECK_EQ(command.get_failed().total(), 0);
209
210 // Human verification
211 fmt::print("Attempted moves are {}\n",
212 command.get_attempted().moves_view());
213 fmt::print("Successful moves are {}\n",
214 command.get_succeeded().moves_view());
215 fmt::print("Failed moves are {}\n", command.get_failed().moves_view());
216 }
217 }
218 }
219}
220
221SCENARIO("Queueing and executing moves" * doctest::test_suite("move_command"))
222{
223 spdlog::debug("Queueing and executing moves.\n");
224 GIVEN("A valid manifold.")
225 {
226 auto constexpr desired_simplices = 9600;
227 auto constexpr desired_timeslices = 7;
228 Manifold_3 manifold(desired_simplices, desired_timeslices);
229 REQUIRE(manifold.is_correct());
230 WHEN("Move_command copies the manifold and applies the move.")
231 {
232 THEN("The original is not mutated.")
233 {
234 // This copies the manifold into the Move_command
235 MoveCommand command(manifold);
236 // Note: If we do a move that expands the size of the manifold,
237 // without the copy ctor this will Segfault!
238 command.enqueue(move_tracker::move_type::THREE_TWO);
239
240 // Execute the move
241 command.execute();
242
243 // An attempted move was recorded
244 CHECK_EQ(command.get_attempted().three_two_moves(), 1);
245
246 // A successful move was recorded
247 CHECK_EQ(command.get_succeeded().three_two_moves(), 1);
248
249 // No failures
250 CHECK_EQ(command.get_failed().three_two_moves(), 0);
251
252 // Get the results
253 auto result = command.get_results();
254
255 // Distinct objects?
256 auto* manifold_ptr = &manifold;
257 auto* result_ptr = &result;
258 REQUIRE_FALSE(manifold_ptr == result_ptr);
259 fmt::print(
260 "The manifold and the result in the MoveCommand are distinct "
261 "pointers.\n");
262
263 // The move should not change the original manifold
264 CHECK_NE(manifold.N3_22(), result.N3_22());
265 CHECK_NE(manifold.N1_TL(), result.N1_TL());
266 fmt::print("The original manifold is unchanged by Move_command.\n");
267 }
268 }
269 WHEN("A (4,4) move is queued.")
270 {
271 MoveCommand command(manifold);
272 command.enqueue(move_tracker::move_type::FOUR_FOUR);
273 THEN("It is executed correctly.")
274 {
275 // Execute the move
276 command.execute();
277
278 // An attempted move was recorded
279 CHECK_EQ(command.get_attempted().four_four_moves(), 1);
280
281 // A successful move was recorded
282 CHECK_EQ(command.get_succeeded().four_four_moves(), 1);
283
284 // No failures
285 CHECK_EQ(command.get_failed().four_four_moves(), 0);
286
287 // Get the results
288 auto const& result = command.get_results();
289
290 // Triangulation shouldn't have changed
291 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
292 manifold.get_triangulation().number_of_finite_cells());
293 REQUIRE(ergodic_moves::check_move(manifold, result,
294 move_tracker::move_type::FOUR_FOUR));
295 fmt::print("Move left triangulation unchanged.\n");
296 }
297 }
298 WHEN("A (2,3) move is queued.")
299 {
300 MoveCommand command(manifold);
301 command.enqueue(move_tracker::move_type::TWO_THREE);
302 THEN("It is executed correctly.")
303 {
304 // Execute the move
305 command.execute();
306
307 // An attempted move was recorded
308 CHECK_EQ(command.get_attempted().two_three_moves(), 1);
309
310 // A successful move was recorded
311 CHECK_EQ(command.get_succeeded().two_three_moves(), 1);
312
313 // No failures
314 CHECK_EQ(command.get_failed().two_three_moves(), 0);
315
316 // Get the results
317 auto const& result = command.get_const_results();
318
319 // Did the triangulation actually change? We should have +1 cell
320 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
321 manifold.get_triangulation().number_of_finite_cells() + 1);
322 REQUIRE(ergodic_moves::check_move(manifold, result,
323 move_tracker::move_type::TWO_THREE));
324 fmt::print("Triangulation added a finite cell.\n");
325 }
326 }
327 WHEN("A (3,2) move is queued.")
328 {
329 MoveCommand command(manifold);
330 command.enqueue(move_tracker::move_type::THREE_TWO);
331 THEN("It is executed correctly.")
332 {
333 // Execute the move
334 command.execute();
335
336 // An attempted move was recorded
337 CHECK_EQ(command.get_attempted().three_two_moves(), 1);
338
339 // A successful move was recorded
340 CHECK_EQ(command.get_succeeded().three_two_moves(), 1);
341
342 // No failures
343 CHECK_EQ(command.get_failed().three_two_moves(), 0);
344
345 // Get the results
346 auto const& result = command.get_const_results();
347
348 // Did the triangulation actually change? We should have -1 cell
349 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
350 manifold.get_triangulation().number_of_finite_cells() - 1);
351 REQUIRE(ergodic_moves::check_move(manifold, result,
352 move_tracker::move_type::THREE_TWO));
353 fmt::print("Triangulation removed a finite cell.\n");
354 }
355 }
356 WHEN("A (2,6) move is queued.")
357 {
358 MoveCommand command(manifold);
359 command.enqueue(move_tracker::move_type::TWO_SIX);
360 THEN("It is executed correctly.")
361 {
362 // Execute the move
363 command.execute();
364
365 // An attempted move was recorded
366 CHECK_EQ(command.get_attempted().two_six_moves(), 1);
367
368 // A successful move was recorded
369 CHECK_EQ(command.get_succeeded().two_six_moves(), 1);
370
371 // No failures
372 CHECK_EQ(command.get_failed().two_six_moves(), 0);
373
374 // Get the results
375 auto const& result = command.get_const_results();
376
377 // Did the triangulation actually change? We should have +4 cell
378 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
379 manifold.get_triangulation().number_of_finite_cells() + 4);
380 REQUIRE(ergodic_moves::check_move(manifold, result,
381 move_tracker::move_type::TWO_SIX));
382 fmt::print("Triangulation added 4 finite cells.\n");
383 }
384 }
385 WHEN("A (6,2) move is queued.")
386 {
387 MoveCommand command(manifold);
388 command.enqueue(move_tracker::move_type::SIX_TWO);
389 THEN("It is executed correctly.")
390 {
391 // Execute the move
392 command.execute();
393
394 // An attempted move was recorded
395 CHECK_EQ(command.get_attempted().six_two_moves(), 1);
396
397 // A successful move was recorded
398 CHECK_EQ(command.get_succeeded().six_two_moves(), 1);
399
400 // No failures
401 CHECK_EQ(command.get_failed().six_two_moves(), 0);
402
403 // Get the results
404 auto const& result = command.get_const_results();
405
406 // Did the triangulation actually change? We should have -1 cell
407 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
408 manifold.get_triangulation().number_of_finite_cells() - 4);
409 CHECK(ergodic_moves::check_move(manifold, result,
410 move_tracker::move_type::SIX_TWO));
411 fmt::print("Triangulation removed 4 finite cells.\n");
412 }
413 }
414 }
415}
416SCENARIO("Executing multiple moves on the queue" *
417 doctest::test_suite("move_command"))
418{
419 spdlog::debug("Executing multiple moves on the queue.\n");
420 GIVEN("A valid manifold")
421 {
422 auto constexpr desired_simplices = 9600;
423 auto constexpr desired_timeslices = 7;
424 Manifold_3 const manifold(desired_simplices, desired_timeslices);
425 REQUIRE(manifold.is_correct());
426 WHEN("(2,3) and (3,2) moves are queued.")
427 {
428 MoveCommand command(manifold);
429 command.enqueue(move_tracker::move_type::TWO_THREE);
430 command.enqueue(move_tracker::move_type::THREE_TWO);
431 THEN("There are two moves in the queue.") { CHECK_EQ(command.size(), 2); }
432 THEN("The moves are executed correctly.")
433 {
434 // Execute the moves
435 command.execute();
436
437 // There should be 2 attempted moves
438 CHECK_EQ(command.get_attempted().total(), 2);
439 command.print_attempts();
440
441 // There should be a successful (2,3) move
442 auto successful_23_moves = command.get_succeeded().two_three_moves();
443 CHECK_EQ(successful_23_moves, 1);
444 fmt::print("There was {} successful (2,3) move.\n",
445 successful_23_moves);
446
447 // There should be a successful (3,2) move
448 auto successful_32_moves = command.get_succeeded().three_two_moves();
449 CHECK_EQ(successful_32_moves, 1);
450 fmt::print("There was {} successful (3,2) move.\n",
451 successful_32_moves);
452
453 // There should be no failed moves
454 CHECK_EQ(command.get_failed().total(), 0);
455 command.print_errors();
456
457 // Get the results
458 auto const& result = command.get_const_results();
459
460 // The moves should cancel out
461 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
462 manifold.get_triangulation().number_of_finite_cells());
463 REQUIRE(ergodic_moves::check_move(manifold, result,
464 move_tracker::move_type::FOUR_FOUR));
465 fmt::print("Triangulation moves cancelled out.");
466 }
467 }
468 WHEN("One of each move is queued.")
469 {
470 MoveCommand command(manifold);
471 command.enqueue(move_tracker::move_type::TWO_THREE);
472 command.enqueue(move_tracker::move_type::TWO_SIX);
473 command.enqueue(move_tracker::move_type::FOUR_FOUR);
474 command.enqueue(move_tracker::move_type::SIX_TWO);
475 command.enqueue(move_tracker::move_type::THREE_TWO);
476 THEN("There are five moves in the queue.")
477 {
478 CHECK_EQ(command.size(), 5);
479 }
480 THEN("The moves are executed correctly.")
481 {
482 // Execute the moves
483 command.execute();
484
485 // There should be 5 attempted moves
486 CHECK_EQ(command.get_attempted().total(), 5);
487 command.print_attempts();
488
489 // There should be a successful (2,3) move
490 auto successful_23_moves = command.get_succeeded().two_three_moves();
491 CHECK_EQ(successful_23_moves, 1);
492 fmt::print("There was {} successful (2,3) move.\n",
493 successful_23_moves);
494
495 // There should be a successful (2,6) move
496 auto successful_26_moves = command.get_succeeded().two_six_moves();
497 CHECK_EQ(successful_26_moves, 1);
498 fmt::print("There was {} successful (2,6) move.\n",
499 successful_26_moves);
500
501 // There should be a successful (4,4) move
502 auto successful_44_moves = command.get_succeeded().four_four_moves();
503 CHECK_EQ(successful_44_moves, 1);
504 fmt::print("There was {} successful (4,4) move.\n",
505 successful_44_moves);
506
507 // There should be a successful (6,2) move
508 auto successful_62_moves = command.get_succeeded().six_two_moves();
509 CHECK_EQ(successful_62_moves, 1);
510 fmt::print("There was {} successful (6,2) move.\n",
511 successful_62_moves);
512
513 // There should be a successful (3,2) move
514 auto successful_32_moves = command.get_succeeded().three_two_moves();
515 CHECK_EQ(successful_32_moves, 1);
516 fmt::print("There was {} successful (3,2) move.\n",
517 successful_32_moves);
518
519 // There should be no failed moves
520 CHECK_EQ(command.get_failed().total(), 0);
521 command.print_errors();
522
523 // Get the results
524 auto const& result = command.get_const_results();
525
526 // The moves should cancel out
527 CHECK_EQ(result.get_triangulation().number_of_finite_cells(),
528 manifold.get_triangulation().number_of_finite_cells());
529 REQUIRE(ergodic_moves::check_move(manifold, result,
530 move_tracker::move_type::FOUR_FOUR));
531 fmt::print("Triangulation moves cancelled out.");
532 }
533 }
534 }
535}
auto constexpr apply_move(ManifoldType &&t_manifold, FunctionType t_move) noexcept -> decltype(auto)
An applicative function similar to std::apply on a manifold.
Definition: Apply_move.hpp:32
auto do_23_move(Manifold &t_manifold) -> Expected
Perform a (2,3) move.
Do ergodic moves using the Command pattern.
SCENARIO("Perform bistellar flip on Delaunay triangulation" *doctest::test_suite("bistellar"))