CDT++
Causal Dynamical Triangulations in C++
Loading...
Searching...
No Matches
Metropolis_test.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Causal Dynamical Triangulations in C++ using CGAL
3
4 Copyright © 2021 Adam Getchell
5 ******************************************************************************/
6
10
11#include "Metropolis.hpp"
12
13#include <doctest/doctest.h>
14
15using namespace std;
16using namespace manifolds;
17
18SCENARIO("MoveStrategy<METROPOLIS> special member and swap properties" *
19 doctest::test_suite("metropolis"))
20{
21 spdlog::debug(
22 "MoveStrategy<METROPOLIS> special member and swap properties.\n");
23 GIVEN("A Metropolis move strategy.")
24 {
25 WHEN("Special members are examined.")
26 {
27 THEN("It is no-throw destructible.")
28 {
29 REQUIRE(is_nothrow_destructible_v<Metropolis_3>);
30 REQUIRE(is_nothrow_destructible_v<Metropolis_4>);
31 spdlog::debug("It is no-throw destructible.\n");
32 }
33 THEN("It is no-throw default constructible.")
34 {
35 REQUIRE(is_nothrow_default_constructible_v<Metropolis_3>);
36 REQUIRE(is_nothrow_default_constructible_v<Metropolis_4>);
37 spdlog::debug("It is no-throw default constructible.\n");
38 }
39 THEN("It is no-throw copy constructible.")
40 {
41 REQUIRE(is_nothrow_copy_constructible_v<Metropolis_3>);
42 REQUIRE(is_nothrow_copy_constructible_v<Metropolis_4>);
43 spdlog::debug("It is no-throw copy constructible.\n");
44 }
45 THEN("It is no-throw copy assignable.")
46 {
47 REQUIRE(is_nothrow_copy_assignable_v<Metropolis_3>);
48 REQUIRE(is_nothrow_copy_assignable_v<Metropolis_4>);
49 spdlog::debug("It is no-throw copy assignable.\n");
50 }
51 THEN("It is no-throw move constructible.")
52 {
53 REQUIRE(is_nothrow_move_constructible_v<Metropolis_3>);
54 REQUIRE(is_nothrow_move_constructible_v<Metropolis_4>);
55 spdlog::debug("It is no-throw move constructible.\n");
56 }
57 THEN("It is no-throw move assignable.")
58 {
59 REQUIRE(is_nothrow_move_assignable_v<Metropolis_3>);
60 REQUIRE(is_nothrow_move_assignable_v<Metropolis_4>);
61 spdlog::debug("It is no-throw move assignable.\n");
62 }
63 THEN("It is no-throw swappable.")
64 {
65 REQUIRE(is_nothrow_swappable_v<Metropolis_3>);
66 REQUIRE(is_nothrow_swappable_v<Metropolis_4>);
67 spdlog::debug("It is no-throw swappable.\n");
68 }
69 THEN("It is constructible from 5 parameters.")
70 {
71 REQUIRE(is_constructible_v<Metropolis_3, long double, long double,
72 long double, Int_precision, Int_precision>);
73 REQUIRE(is_constructible_v<Metropolis_4, long double, long double,
74 long double, Int_precision, Int_precision>);
75 spdlog::debug("It is constructible from 5 parameters.\n");
76 }
77 }
78 }
79}
80
81SCENARIO("Metropolis member functions" * doctest::test_suite("metropolis"))
82{
83 auto constexpr Alpha = static_cast<long double>(0.6);
84 auto constexpr K = static_cast<long double>(1.1); // NOLINT
85 auto constexpr Lambda = static_cast<long double>(0.1);
86 GIVEN("A correctly-constructed Manifold_3.")
87 {
88 auto constexpr simplices = 640;
89 auto constexpr timeslices = 4;
90 Manifold_3 const universe(simplices, timeslices);
91 // It is correctly constructed
92 REQUIRE(universe.is_correct());
93 WHEN("A Metropolis function object is constructed.")
94 {
95 auto constexpr output_every_n_passes = 1;
96 auto constexpr passes = 10;
97 Metropolis_3 testrun(Alpha, K, Lambda, passes, output_every_n_passes);
98 THEN("The Metropolis function object is initialized correctly.")
99 {
100 CHECK_EQ(testrun.Alpha(), Alpha);
101 CHECK_EQ(testrun.K(), K);
102 CHECK_EQ(testrun.Lambda(), Lambda);
103 CHECK_EQ(testrun.passes(), passes);
104 CHECK_EQ(testrun.checkpoint(), output_every_n_passes);
105 CHECK_EQ(testrun.get_proposed().total(), 0);
106 CHECK_EQ(testrun.get_accepted().total(), 0);
107 CHECK_EQ(testrun.get_rejected().total(), 0);
108 CHECK_EQ(testrun.get_attempted().total(), 0);
109 CHECK_EQ(testrun.get_succeeded().total(), 0);
110 CHECK_EQ(testrun.get_failed().total(), 0);
111 }
112 THEN("The initial moves are made correctly.")
113 {
114 auto result = testrun.initialize(universe);
115 auto total_rejected = testrun.get_rejected().total();
116 auto total_attempted = testrun.get_attempted().total();
117 auto total_successful = testrun.get_succeeded().total();
118 auto total_failed = testrun.get_failed().total();
119 // Initialization proposes one move of each type
120 for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i)
121 {
122 CHECK_EQ(testrun.get_proposed()[i], 1);
123 }
124 // Initialization accepts one move of each type
125 for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i)
126 {
127 CHECK_EQ(testrun.get_accepted()[i], 1);
128 }
129 // Initialization does not reject any moves
130 CHECK_EQ(total_rejected, 0);
131 // Initialization attempts one move of each type
132 for (auto i = 0; i < move_tracker::NUMBER_OF_3D_MOVES; ++i)
133 {
134 CHECK_EQ(testrun.get_attempted()[i], 1);
135 }
136 CHECK_EQ(total_attempted, total_successful + total_failed);
137
138 // Human verification
139 REQUIRE_MESSAGE(result,
140 "The Metropolis function object failed to "
141 "initialize the universe.");
142 if (result)
143 {
144 result->print_attempts();
145 result->print_successful();
146 result->print_errors();
147 }
148 }
149 }
150 }
151}
152
153SCENARIO("Using the Metropolis algorithm" * doctest::test_suite("metropolis"))
154{
155 auto constexpr Alpha = static_cast<long double>(0.6);
156 auto constexpr K = static_cast<long double>(1.1); // NOLINT
157 auto constexpr Lambda = static_cast<long double>(0.1);
158 GIVEN("A correctly-constructed Manifold_3.")
159 {
160 auto constexpr simplices = 640;
161 auto constexpr timeslices = 4;
162 Manifold_3 const universe(simplices, timeslices);
163 // It is correctly constructed
164 REQUIRE(universe.is_correct());
165 WHEN("A Metropolis function object is constructed.")
166 {
167 auto constexpr output_every_n_passes = 1;
168 auto constexpr passes = 1;
169 Metropolis_3 testrun(Alpha, K, Lambda, passes, output_every_n_passes);
170 THEN("A lot of moves are done.")
171 {
172 auto result = testrun(universe);
173 // Output
174 CHECK(result.is_valid());
175 AND_THEN("The correct number of moves are attempted.")
176 {
177 auto total_proposed = testrun.get_proposed().total();
178 auto total_accepted = testrun.get_accepted().total();
179 auto total_rejected = testrun.get_rejected().total();
180 auto total_attempted = testrun.get_attempted().total();
181 auto total_successful = testrun.get_succeeded().total();
182 auto total_failed = testrun.get_failed().total();
183 // We should have at least a trial move per simplex on average
184 // per pass, times the number of passes
185 CHECK_GT(total_proposed, universe.N3() * passes);
186 CHECK_EQ(total_proposed, total_accepted + total_rejected);
187 // We should attempt a move for each accepted move
188 CHECK_EQ(total_attempted, total_accepted);
189 CHECK_GT(total_successful, 0);
190 CHECK_GE(total_failed, 0);
191 CHECK_EQ(total_attempted, total_successful + total_failed);
192 // Human verification
193 testrun.print_results();
194 }
195 }
196 }
197 }
198}
Perform Metropolis-Hastings algorithm on Delaunay Triangulations.
std::int_fast32_t Int_precision
Definition: Settings.hpp:31
SCENARIO("Perform bistellar flip on Delaunay triangulation" *doctest::test_suite("bistellar"))
Select a move algorithm.