openjij
Framework for the Ising model and QUBO.
Loading...
Searching...
No Matches
continuous_time_swendsen_wang.hpp
Go to the documentation of this file.
1// Copyright 2023 Jij Inc.
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <algorithm>
18#include <cassert>
19#include <cmath>
20#include <random>
21#include <unordered_map>
22#include <vector>
23
24#include "openjij/graph/all.hpp"
28
29namespace openjij {
30namespace updater {
31
37template <typename System> struct ContinuousTimeSwendsenWang;
38
44template <typename FloatType>
46 system::ContinuousTimeIsing<graph::Sparse<FloatType>>> {
51
56 template <typename RandomNumberEngine>
57 static void
59 RandomNumberEngine &random_number_engine,
61
62 const graph::Index num_spin = system.num_spins;
63 std::vector<graph::Index> index_helper;
64 index_helper.reserve(num_spin + 1);
65 index_helper.push_back(0);
66 /* index_helper[i]+k gives 1 dimensionalized index of kth time point at ith
67 * site. this helps use of union-find tree only available for 1D structure.
68 */
69
70 /* 1. remove old cuts and place new cuts for every site */
71 for (graph::Index i = 0; i < num_spin; i++) {
72 auto &timeline = system.spin_config[i];
73 auto cuts =
74 generate_poisson_points(0.5 * system.gamma * (1.0 - parameter.s),
75 parameter.beta, random_number_engine);
76 // assuming transverse field gamma is positive
77
78 timeline = create_timeline(timeline, cuts);
79 assert(timeline.size() > 0);
80
81 index_helper.push_back(index_helper.back() + timeline.size());
82 }
83
84 /* 2. place spacial bonds */
85 utility::UnionFind union_find_tree(index_helper.back());
86 for (graph::Index i = 0; i < num_spin; i++) {
87 for (typename CTIsing::SparseMatrixXx::InnerIterator it(
88 system.interaction, i);
89 it; ++it) {
90 std::size_t j = it.index();
91 const FloatType &J = it.value();
92 if (i < j) {
93 continue; // ignore duplicated interaction
94 // if adj_nodes are sorted, this "continue" can be replaced
95 // by "break"
96 }
97
98 const auto bonds =
99 generate_poisson_points(std::abs(0.5 * J * parameter.s),
100 parameter.beta, random_number_engine);
101 for (const auto bond : bonds) {
102 /* get time point indices just before the bond */
103 auto ki = system.get_temporal_spin_index(i, bond);
104 auto kj = system.get_temporal_spin_index(j, bond);
105
106 if (system.spin_config[i][ki].second *
107 system.spin_config[j][kj].second * J <
108 0) {
109 union_find_tree.unite_sets(index_helper[i] + ki,
110 index_helper[j] + kj);
111 }
112 }
113 }
114 }
115
116 /* 3. make clusters */
117 std::unordered_map<utility::UnionFind::Node,
118 std::vector<utility::UnionFind::Node>>
119 cluster_map;
120 // root-index to {contained nodes} map
121
122 for (graph::Index i = 0; i < num_spin; i++) {
123 for (size_t k = 0; k < system.spin_config[i].size(); k++) {
124 auto index = index_helper[i] + k;
125 auto root_index = union_find_tree.find_set(index);
126 auto position = cluster_map.find(root_index);
127 if (position == cluster_map.end()) {
128 cluster_map.emplace(root_index,
129 std::vector<utility::UnionFind::Node>{index});
130 } else {
131 position->second.push_back(index);
132 }
133 }
134 }
135
136 /* 4. flip clusters */
137 auto urd = std::uniform_real_distribution<>(0, 1.0);
138 for (const auto &cluster : cluster_map) {
139 // 4.1. decide spin state (flip with the probability 1/2)
140 const FloatType probability = 1.0 / 2.0;
141 if (urd(random_number_engine) < probability) {
142 // 4.2. update spin states
143 for (auto node : cluster.second) {
144 auto i = std::distance(index_helper.begin(),
145 std::upper_bound(index_helper.begin(),
146 index_helper.end(), node)) -
147 1;
148 auto k = node - index_helper[i];
149 system.spin_config[i][k].second *= -1;
150 }
151 }
152 }
153 }
154
160 static std::vector<CutPoint>
161 create_timeline(const std::vector<CutPoint> &old_timeline,
162 const std::vector<TimeType> &cuts) {
163 /* remove redundant cuts*/
164 std::vector<CutPoint> concatenated_timeline;
165 auto current_spin = old_timeline.back().second;
166 for (auto cut_point : old_timeline) {
167 if (cut_point.second != current_spin) {
168 concatenated_timeline.push_back(cut_point);
169 }
170 current_spin = cut_point.second;
171 }
172
173 /* if entire timeline is occupied by single spin state */
174 std::vector<CutPoint> new_timeline;
175 if (concatenated_timeline.empty()) {
176 if (cuts.empty()) {
177 new_timeline.push_back(old_timeline[0]);
178 } else {
179 for (auto cut : cuts) {
180 new_timeline.push_back(CutPoint(cut, current_spin));
181 }
182 }
183 return new_timeline;
184 }
185
186 current_spin = concatenated_timeline.back().second;
187 auto timeline_itr = concatenated_timeline.begin();
188 auto cuts_itr = cuts.begin();
189 while (true) {
190 /* if all cuts have been placed, add remaining old kinks and break loop */
191 if (cuts_itr == cuts.end()) {
192 std::for_each(timeline_itr, concatenated_timeline.end(),
193 [&](CutPoint it) { new_timeline.push_back(it); });
194 break;
195 }
196
197 /* if all spin kinks have been placed, add remaining cuts and break loop
198 */
199 if (timeline_itr == concatenated_timeline.end()) {
200 std::for_each(cuts_itr, cuts.end(), [&](TimeType it) {
201 new_timeline.push_back(CutPoint(it, current_spin));
202 });
203 break;
204 }
205
206 /* add earlier of kink or cut to new timeline */
207 if (*cuts_itr < timeline_itr->first) {
208 new_timeline.push_back(CutPoint(*cuts_itr, current_spin));
209 cuts_itr++;
210 } else {
211 new_timeline.push_back(*timeline_itr);
212 current_spin = timeline_itr->second;
213 timeline_itr++;
214 }
215 }
216
217 return new_timeline;
218 }
219
224 static std::vector<CutPoint>
225 create_timeline_easy(const std::vector<CutPoint> &old_timeline,
226 const std::vector<TimeType> &cuts) {
227 /* remove redundant cuts*/
228 std::vector<CutPoint> new_timeline;
229 auto current_spin = old_timeline.back().second;
230 for (auto cut_point : old_timeline) {
231 if (cut_point.second != current_spin) {
232 new_timeline.push_back(cut_point);
233 }
234 current_spin = cut_point.second;
235 }
236
237 /* if entire timeline is occupied by single spin state */
238 if (new_timeline.empty()) {
239 if (cuts.empty()) {
240 new_timeline.push_back(old_timeline[0]);
241 } else {
242 for (auto cut : cuts) {
243 new_timeline.push_back(CutPoint(cut, current_spin));
244 }
245 }
246 return new_timeline;
247 }
248
249 static const auto first_lt = [](CutPoint x, CutPoint y) {
250 return x.first < y.first;
251 };
252 for (auto cut : cuts) {
253 const auto dummy_cut = CutPoint(cut, 0); // dummy cut for binary search
254 auto itr = std::upper_bound(new_timeline.begin(), new_timeline.end(),
255 dummy_cut, first_lt);
256 auto prev_itr =
257 (itr == new_timeline.begin()) ? new_timeline.end() - 1 : itr - 1;
258
259 new_timeline.insert(itr, CutPoint(cut, prev_itr->second));
260 }
261
262 return new_timeline;
263 }
264
271 template <typename RandomNumberEngine>
272 static std::vector<TimeType>
273 generate_poisson_points(const TimeType lambda, const TimeType beta,
274 RandomNumberEngine &random_number_engine) {
275 std::uniform_real_distribution<> rand(0.0, 1.0);
276 std::uniform_real_distribution<> rand_beta(0.0, beta);
277
278 const TimeType coef = beta * lambda;
279 TimeType n = 0;
280 TimeType d = std::exp(-coef);
281 TimeType p = d;
282 TimeType xi = rand(random_number_engine);
283
284 while (p < xi) {
285 n += 1;
286 d *= coef / n;
287 p += d;
288 }
289
290 std::vector<TimeType> poisson_points(n);
291 for (int k = 0; k < n; k++) {
292 poisson_points[k] = rand_beta(random_number_engine);
293 }
294 std::sort(poisson_points.begin(), poisson_points.end());
295
296 return poisson_points;
297 }
298};
299
305template <typename FloatType>
307 system::ContinuousTimeIsing<graph::CSRSparse<FloatType>>> {
312
313 //TODO: duplicate code of `system::ContinuousTimeIsing<graph::Sparse<FloatType>>>`, need fix.
314
319 template <typename RandomNumberEngine>
320 static void
322 RandomNumberEngine &random_number_engine,
324
325 const graph::Index num_spin = system.num_spins;
326 std::vector<graph::Index> index_helper;
327 index_helper.reserve(num_spin + 1);
328 index_helper.push_back(0);
329 /* index_helper[i]+k gives 1 dimensionalized index of kth time point at ith
330 * site. this helps use of union-find tree only available for 1D structure.
331 */
332
333 /* 1. remove old cuts and place new cuts for every site */
334 for (graph::Index i = 0; i < num_spin; i++) {
335 auto &timeline = system.spin_config[i];
336 auto cuts =
337 generate_poisson_points(0.5 * system.gamma * (1.0 - parameter.s),
338 parameter.beta, random_number_engine);
339 // assuming transverse field gamma is positive
340
341 timeline = create_timeline(timeline, cuts);
342 assert(timeline.size() > 0);
343
344 index_helper.push_back(index_helper.back() + timeline.size());
345 }
346
347 /* 2. place spacial bonds */
348 utility::UnionFind union_find_tree(index_helper.back());
349 for (graph::Index i = 0; i < num_spin; i++) {
350 for (typename CTIsing::SparseMatrixXx::InnerIterator it(
351 system.interaction, i);
352 it; ++it) {
353 std::size_t j = it.index();
354 const FloatType &J = it.value();
355 if (i < j) {
356 continue; // ignore duplicated interaction
357 // if adj_nodes are sorted, this "continue" can be replaced
358 // by "break"
359 }
360
361 const auto bonds =
362 generate_poisson_points(std::abs(0.5 * J * parameter.s),
363 parameter.beta, random_number_engine);
364 for (const auto bond : bonds) {
365 /* get time point indices just before the bond */
366 auto ki = system.get_temporal_spin_index(i, bond);
367 auto kj = system.get_temporal_spin_index(j, bond);
368
369 if (system.spin_config[i][ki].second *
370 system.spin_config[j][kj].second * J <
371 0) {
372 union_find_tree.unite_sets(index_helper[i] + ki,
373 index_helper[j] + kj);
374 }
375 }
376 }
377 }
378
379 /* 3. make clusters */
380 std::unordered_map<utility::UnionFind::Node,
381 std::vector<utility::UnionFind::Node>>
382 cluster_map;
383 // root-index to {contained nodes} map
384
385 for (graph::Index i = 0; i < num_spin; i++) {
386 for (size_t k = 0; k < system.spin_config[i].size(); k++) {
387 auto index = index_helper[i] + k;
388 auto root_index = union_find_tree.find_set(index);
389 auto position = cluster_map.find(root_index);
390 if (position == cluster_map.end()) {
391 cluster_map.emplace(root_index,
392 std::vector<utility::UnionFind::Node>{index});
393 } else {
394 position->second.push_back(index);
395 }
396 }
397 }
398
399 /* 4. flip clusters */
400 auto urd = std::uniform_real_distribution<>(0, 1.0);
401 for (const auto &cluster : cluster_map) {
402 // 4.1. decide spin state (flip with the probability 1/2)
403 const FloatType probability = 1.0 / 2.0;
404 if (urd(random_number_engine) < probability) {
405 // 4.2. update spin states
406 for (auto node : cluster.second) {
407 auto i = std::distance(index_helper.begin(),
408 std::upper_bound(index_helper.begin(),
409 index_helper.end(), node)) -
410 1;
411 auto k = node - index_helper[i];
412 system.spin_config[i][k].second *= -1;
413 }
414 }
415 }
416 }
417
423 static std::vector<CutPoint>
424 create_timeline(const std::vector<CutPoint> &old_timeline,
425 const std::vector<TimeType> &cuts) {
426 /* remove redundant cuts*/
427 std::vector<CutPoint> concatenated_timeline;
428 auto current_spin = old_timeline.back().second;
429 for (auto cut_point : old_timeline) {
430 if (cut_point.second != current_spin) {
431 concatenated_timeline.push_back(cut_point);
432 }
433 current_spin = cut_point.second;
434 }
435
436 /* if entire timeline is occupied by single spin state */
437 std::vector<CutPoint> new_timeline;
438 if (concatenated_timeline.empty()) {
439 if (cuts.empty()) {
440 new_timeline.push_back(old_timeline[0]);
441 } else {
442 for (auto cut : cuts) {
443 new_timeline.push_back(CutPoint(cut, current_spin));
444 }
445 }
446 return new_timeline;
447 }
448
449 current_spin = concatenated_timeline.back().second;
450 auto timeline_itr = concatenated_timeline.begin();
451 auto cuts_itr = cuts.begin();
452 while (true) {
453 /* if all cuts have been placed, add remaining old kinks and break loop */
454 if (cuts_itr == cuts.end()) {
455 std::for_each(timeline_itr, concatenated_timeline.end(),
456 [&](CutPoint it) { new_timeline.push_back(it); });
457 break;
458 }
459
460 /* if all spin kinks have been placed, add remaining cuts and break loop
461 */
462 if (timeline_itr == concatenated_timeline.end()) {
463 std::for_each(cuts_itr, cuts.end(), [&](TimeType it) {
464 new_timeline.push_back(CutPoint(it, current_spin));
465 });
466 break;
467 }
468
469 /* add earlier of kink or cut to new timeline */
470 if (*cuts_itr < timeline_itr->first) {
471 new_timeline.push_back(CutPoint(*cuts_itr, current_spin));
472 cuts_itr++;
473 } else {
474 new_timeline.push_back(*timeline_itr);
475 current_spin = timeline_itr->second;
476 timeline_itr++;
477 }
478 }
479
480 return new_timeline;
481 }
482
487 static std::vector<CutPoint>
488 create_timeline_easy(const std::vector<CutPoint> &old_timeline,
489 const std::vector<TimeType> &cuts) {
490 /* remove redundant cuts*/
491 std::vector<CutPoint> new_timeline;
492 auto current_spin = old_timeline.back().second;
493 for (auto cut_point : old_timeline) {
494 if (cut_point.second != current_spin) {
495 new_timeline.push_back(cut_point);
496 }
497 current_spin = cut_point.second;
498 }
499
500 /* if entire timeline is occupied by single spin state */
501 if (new_timeline.empty()) {
502 if (cuts.empty()) {
503 new_timeline.push_back(old_timeline[0]);
504 } else {
505 for (auto cut : cuts) {
506 new_timeline.push_back(CutPoint(cut, current_spin));
507 }
508 }
509 return new_timeline;
510 }
511
512 static const auto first_lt = [](CutPoint x, CutPoint y) {
513 return x.first < y.first;
514 };
515 for (auto cut : cuts) {
516 const auto dummy_cut = CutPoint(cut, 0); // dummy cut for binary search
517 auto itr = std::upper_bound(new_timeline.begin(), new_timeline.end(),
518 dummy_cut, first_lt);
519 auto prev_itr =
520 (itr == new_timeline.begin()) ? new_timeline.end() - 1 : itr - 1;
521
522 new_timeline.insert(itr, CutPoint(cut, prev_itr->second));
523 }
524
525 return new_timeline;
526 }
527
534 template <typename RandomNumberEngine>
535 static std::vector<TimeType>
536 generate_poisson_points(const TimeType lambda, const TimeType beta,
537 RandomNumberEngine &random_number_engine) {
538 std::uniform_real_distribution<> rand(0.0, 1.0);
539 std::uniform_real_distribution<> rand_beta(0.0, beta);
540
541 const TimeType coef = beta * lambda;
542 TimeType n = 0;
543 TimeType d = std::exp(-coef);
544 TimeType p = d;
545 TimeType xi = rand(random_number_engine);
546
547 while (p < xi) {
548 n += 1;
549 d *= coef / n;
550 p += d;
551 }
552
553 std::vector<TimeType> poisson_points(n);
554 for (int k = 0; k < n; k++) {
555 poisson_points[k] = rand_beta(random_number_engine);
556 }
557 std::sort(poisson_points.begin(), poisson_points.end());
558
559 return poisson_points;
560 }
561};
562} // namespace updater
563} // namespace openjij
CSRSparse graph: just store CSR Sparse Matrix (Eigen::Sparse) The Hamiltonian is like.
Definition csr_sparse.hpp:42
Sparse graph: two-body intereactions with O(1) connectivity The Hamiltonian is like.
Definition sparse.hpp:40
std::size_t Index
Definition graph.hpp:30
Definition algorithm.hpp:24
double FloatType
Note:
Definition compile_config.hpp:37
Continuous Time Quantum Ising system (for CSR Sparse graph)
Definition continuous_time_ising.hpp:260
Continuous Time Quantum Ising system (for Sparse graph)
Definition continuous_time_ising.hpp:42
Continuous Time Quantum Ising system.
Definition continuous_time_ising.hpp:34
static std::vector< CutPoint > create_timeline_easy(const std::vector< CutPoint > &old_timeline, const std::vector< TimeType > &cuts)
easy but inefficient version of create_timeline()
Definition continuous_time_swendsen_wang.hpp:225
typename graph::Sparse< FloatType > GraphType
Definition continuous_time_swendsen_wang.hpp:48
static std::vector< CutPoint > create_timeline(const std::vector< CutPoint > &old_timeline, const std::vector< TimeType > &cuts)
create new timeline; place kinks by ignoring old cuts and place new cuts
Definition continuous_time_swendsen_wang.hpp:161
static std::vector< TimeType > generate_poisson_points(const TimeType lambda, const TimeType beta, RandomNumberEngine &random_number_engine)
generates Poisson points with density lambda in the range of [0:beta)
Definition continuous_time_swendsen_wang.hpp:273
typename system::ContinuousTimeIsing< GraphType >::CutPoint CutPoint
Definition continuous_time_swendsen_wang.hpp:49
static void update(system::ContinuousTimeIsing< GraphType > &system, RandomNumberEngine &random_number_engine, const utility::TransverseFieldUpdaterParameter &parameter)
continuous time Swendsen-Wang updater for transverse ising model
Definition continuous_time_swendsen_wang.hpp:58
typename system::ContinuousTimeIsing< GraphType >::TimeType TimeType
Definition continuous_time_swendsen_wang.hpp:50
static void update(system::ContinuousTimeIsing< GraphType > &system, RandomNumberEngine &random_number_engine, const utility::TransverseFieldUpdaterParameter &parameter)
continuous time Swendsen-Wang updater for transverse ising model
Definition continuous_time_swendsen_wang.hpp:321
static std::vector< CutPoint > create_timeline_easy(const std::vector< CutPoint > &old_timeline, const std::vector< TimeType > &cuts)
easy but inefficient version of create_timeline()
Definition continuous_time_swendsen_wang.hpp:488
typename graph::CSRSparse< FloatType > GraphType
Definition continuous_time_swendsen_wang.hpp:309
typename system::ContinuousTimeIsing< GraphType >::CutPoint CutPoint
Definition continuous_time_swendsen_wang.hpp:310
typename system::ContinuousTimeIsing< GraphType >::TimeType TimeType
Definition continuous_time_swendsen_wang.hpp:311
static std::vector< CutPoint > create_timeline(const std::vector< CutPoint > &old_timeline, const std::vector< TimeType > &cuts)
create new timeline; place kinks by ignoring old cuts and place new cuts
Definition continuous_time_swendsen_wang.hpp:424
static std::vector< TimeType > generate_poisson_points(const TimeType lambda, const TimeType beta, RandomNumberEngine &random_number_engine)
generates Poisson points with density lambda in the range of [0:beta)
Definition continuous_time_swendsen_wang.hpp:536
Continuous Time Swendsen Wang updater.
Definition continuous_time_swendsen_wang.hpp:37
Definition union_find.hpp:24
Node find_set(Node node)
Definition union_find.hpp:50
std::size_t Node
Definition union_find.hpp:25
void unite_sets(Node x, Node y)
Definition union_find.hpp:34
updater paramter for transverse ising model
Definition schedule_list.hpp:74
double beta
inverse temperature
Definition schedule_list.hpp:85
double s
annealing schedule (from 0 (only transverse field) to 1 (only classical Hamiltonian))
Definition schedule_list.hpp:91