HILA
Loading...
Searching...
No Matches
multicanonical.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2/// @file multicanonical.cpp
3/// @author Jaakko Hällfors
4/// @brief Model agnostic implementation of various multicanonical methods
5/// @details TBA
6////////////////////////////////////////////////////////////////////////////////
7#include <stdio.h>
8#include <vector>
9#include <algorithm>
10#include <regex>
11#include <cmath>
12#include "hila.h"
14
15using string = std::string;
16using int_vector = std::vector<int>;
17using vector = std::vector<double>;
18
20struct direct_iteration;
22
23typedef bool (* finish_condition_pointer)(std::vector<int> &visits);
24
25// Structs to encompass the various options related to
26// iteration of the multicanonical weights.
27////////////////////////////////////////////////////////////////////////////////
28/// @struct canonical_iteration
29/// @brief An internal struct parametrising the canonical weight
30/// iteration method.
31///
32/// @var int canonical_iteration::sample_size
33/// @brief Number of samples before weight function update
34///
35/// @var int canonical_iteration::initial_bin_hits
36/// @brief Initial number of entries in the bin totals
37/// @details Larger number makes the weight less susceptible to changes in the
38/// following iterations.
39///
40/// @var int canonical_iteration::OC_max_iter
41/// @brief Up to which iteration the overcorrection updates can be used
42///
43/// @var int canonical_iteration::OC_frequency
44/// @brief How often the overcorrection update is used
45/// @details If the value is n, every n:th update will be an overcorrection.
46///////////////////////////////////////////////////////////////////////////////
48{
53};
54
55////////////////////////////////////////////////////////////////////////////////
56/// @struct direct_iteration
57/// @brief An internal struct parametrising the direct weight iteration method.
58///
59/// @var string direct_iteration::finish_condition
60/// @brief Determines the iteration condition for the iteration method.
61/// @details Current options: "all_visited", "ends_visited". The weight
62/// modification factor \f$C\f$ is decreased after set bins are visited. These
63/// can include e.g. all bins, or just the ends (the two preset options).
64/// Finishing conditions can also be determined by the user and passed to the
65/// methods through muca::direct_iteration_finish_condition(&func_pointer)
66///
67/// @var int direct_iteration::sample_size
68/// @brief Number of samples before weight function update
69///
70/// @var int direct_iteration::single_check_interval
71/// @brief Determines how often the update condition is checked for the case
72/// direct_iteration::sample_size = 1
73///
74/// @var double direct_iteration::C_init
75/// @brief Initial magnitude of the weight update
76///
77/// @var double direct_iteration::C_min
78/// @brief Minimum magnitude of the weight update
79///
80/// @var double direct_iteration::C
81/// @brief Magnitude of the weight update
82/// @details This entry is decreased through the direct iteration method until
83/// \f$ C < C_\mathrm{min}\f$ at which point the iteration is considered
84/// complete. The magnitude of the update is such that the mean weight
85/// modification is \f$C\f$. That is, the update satisfies
86/// \f{equation}{\sum_i \delta W_i = N C,\f}
87/// where \f$N\f$. is the number of bins. For sample_size = 1 we simply add
88/// \f$ C \f$ to the single relevant bin each iteration.
89///////////////////////////////////////////////////////////////////////////////
91{
95 double C_init;
96 double C_min;
97 double C;
98};
99
100////////////////////////////////////////////////////////////////////////////////
101/// @struct weight_iteration_parameters
102/// @brief An internal struct parametrising multicanonical methods.
103///
104/// @var string weight_iteration_parameters::weight_loc
105/// @brief Path to the weight function file
106///
107/// @var string weight_iteration_parameters::outfile_name_base
108/// @brief Prefix for the saved weight function files
109/// @details
110///
111/// @var string weight_iteration_parameters::method
112/// @brief Name of the iteration method
113/// @details Current options: "direct"
114/// See the documentation for the details of different methods.
115///
116/// @var bool weight_iteration_parameters::visuals
117/// @brief Whether to print histogram during iteration
118///
119/// @var bool weight_iteration_parameters::hard_walls
120/// @brief Whether the weight outside max_OP and min_OP is infinite
121/// @details If not, the weight is assigned through constant extrapolation of
122/// the nearest bin (first or last). Please start your simulations so that the
123/// first configuration is within the interval, or the iteration can get stuck.
124///
125/// @var double weight_iteration_parameters::max_OP
126/// @brief Maximum order parameter value
127/// @details Only matters when creating bins from given parameters. When the
128/// details of the weights are read from file this parameter is not used.
129///
130/// @var double weight_iteration_parameters::min_OP
131/// @brief Minimum order parameter value
132/// @details Only matters when creating bins from given parameters. When the
133/// details of the weights are read from file this parameter is not used.
134///
135/// @var int weight_iteration_parameters::bin_number
136/// @brief Number of OP bins
137/// @details Only matters when creating bins from given parameters. When the
138/// details of the weights are read from file this parameter is not used.
139///
140/// @var bool weight_iteration_parameters::AR_iteration
141/// @brief Whether to update the weights after each call to accept_reject
142///
143/// @var struct direct_iteration weight_iteration_parameters::DIP
144/// @brief A substruct that contains method specific parameters
145///
146/// @var struct canonical_iteration weight_iteration_parameters::CIP
147/// @brief A substruct that contains method specific parameters
148///////////////////////////////////////////////////////////////////////////////
150{
153 string method;
154
157 double max_OP;
158 double min_OP;
163};
164
165// File global parameter struct filled by read_weight_parameters
166static weight_iteration_parameters g_WParam;
167
168// Initialise some static vectors for this file only.
169static vector g_OPValues(1,0);
170static vector g_OPBinLimits(2,0);
171static vector g_WValues(1,0);
172static int_vector g_N_OP_Bin(1,0);
173static int_vector g_N_OP_BinTotal(1,0);
174static int g_WeightIterationCount = 0;
175static bool g_WeightIterationFlag = true;
176
177namespace hila
178{
179namespace muca
180{
181
182// Pointer to the iteration function
183iteration_pointer iterate_weights;
184
185// Pointer to the finish condition check
186finish_condition_pointer finish_check;
187
188////////////////////////////////////////////////////////////////////////////////
189/// @brief Writes a variable to the file, given the format string.
190///
191/// @param output_file
192/// @param fmt format string corresponding to input_value
193/// @param input_value numerical value to write to output_file
194////////////////////////////////////////////////////////////////////////////////
195template <class K>
196void to_file(std::ofstream &output_file, string fmt, K input_value)
197{
198 char buffer[1024];
199 sprintf(buffer, fmt.c_str(), input_value);
200 if (hila::myrank() == 0) output_file << string(buffer);
201}
202
203////////////////////////////////////////////////////////////////////////////////
204/// @brief Generates a time stamped and otherwise appropriate file name for the
205/// saved weight function files.
206///
207/// @return generated filename string
208////////////////////////////////////////////////////////////////////////////////
210{
211 string filename = g_WParam.outfile_name_base + "_weight_function_";
212
213 // A terrible mess to get the datetime format nice
214 std::stringstream ss;
215 std::time_t t = std::time(nullptr);
216 std::tm tm = *std::localtime(&t);
217 ss << std::put_time(&tm, "created_%Y.%m.%d_%H:%M:%S");
218 string date = ss.str();
219
220 filename = filename + date;
221 return filename;
222}
223
224////////////////////////////////////////////////////////////////////////////////
225/// @brief Parses the weight parameter file and fills the g_WParam struct.
226/// @details
227///
228/// @param parameter_file_name parameter file name
229////////////////////////////////////////////////////////////////////////////////
230void read_weight_parameters(string parameter_file_name)
231{
232 // Open the weight parameter file and list through the parameters.
233 // See the parameter file for the roles of the parameters.
234 hila::input par(parameter_file_name);
235
236 // Generic control parameters
237 string output_loc = par.get("output file location");
238 string outfile_name_base = par.get("output file name base");
239
240 string weight_loc = par.get("weight file location");
241 string iter_method = par.get("iteration method");
242 string hard_walls = par.get("hard walls");
243 double max_OP = par.get("max OP");
244 double min_OP = par.get("min OP");
245 int bin_number = par.get("bin number");
246 string iter_vis = par.get("iteration visuals");
247
248 // Direct iteration parameters
249 string finish_condition = par.get("finish condition");
250 int DIM_sample_size = par.get("DIM sample size");
251 int DIM_check_interval = par.get("DIM visit check interval");
252 double add_initial = par.get("add initial");
253 double add_minimum = par.get("add minimum");
254
255 // Canonical iteration parameters
256 int CIM_sample_size = par.get("CIM sample size");
257 int initial_bin_hits = par.get("initial bin hits");
258 int OC_max_iter = par.get("OC max iter");
259 int OC_frequency = par.get("OC frequency");
260
261 par.close();
262
263 struct direct_iteration DIP
264 {
265 finish_condition,
266 DIM_sample_size,
267 DIM_check_interval,
268 add_initial,
269 add_minimum,
270 add_initial
271 };
272
273 struct canonical_iteration CIP
274 {
275 CIM_sample_size,
276 initial_bin_hits,
277 OC_max_iter,
278 OC_frequency
279 };
280
281 bool AR_ITER = false;
282
283 bool visuals;
284 if (iter_vis.compare("YES") == 0)
285 visuals = true;
286 else visuals = false;
287
288 bool hwalls;
289 if (hard_walls.compare("YES") == 0)
290 hwalls = true;
291 else hwalls = false;
292
293 g_WParam =
294 {
295 weight_loc,
296 outfile_name_base,
297 iter_method,
298 visuals,
299 hwalls,
300 max_OP,
301 min_OP,
302 bin_number,
303 AR_ITER,
304 DIP,
305 CIP
306 };
307}
308
309////////////////////////////////////////////////////////////////////////////////
310/// @brief
311/// Reads a precomputed weight function from file.
312/// @details
313/// The input file is to have three tab-separated columns:
314/// 1. bin edges, of length n + 1
315/// 2. bin centres, of length n
316/// 3. weight values, of length n
317///
318/// Naturally, column 2 can be determined from the first one
319/// but they are considered a separated input anyways.
320///
321/// The header can be whatever* and is always skipped. Regex finds the
322/// data by finding first row with the substring "OP_value" and
323/// assumes that the following contains the data as specified above.
324///
325/// *Avoid substring "OP_value"
326///
327/// @param W_function_filename
328////////////////////////////////////////////////////////////////////////////////
329void read_weight_function(string W_function_filename)
330{
331 hila::out0 << "\nLoading the user supplied weight function.\n";
332 // Compute first header length by counting lines until finding
333 // the column header through regex.
334 if (hila::myrank() == 0)
335 {
336 int header_length = 1, data_length = -1;
337 std::ifstream W_file;
338 W_file.open(W_function_filename.c_str());
339 if (W_file.is_open())
340 {
341 string line;
342 while(std::getline(W_file, line))
343 {
344 if (std::regex_match(line, std::regex(".*OP_value.*")))
345 data_length = 0;
346
347 if (data_length < 0)
348 header_length += 1;
349 else
350 data_length += 1;
351 }
352 }
353 W_file.close();
354 W_file.open(W_function_filename.c_str());
355
356 // Skip the header and sscanf the values and add them into the vectors.
357 int count = - header_length;
358 data_length -= 1;
359 printf("Weight function has header length of %d rows.\n",
360 header_length);
361 printf("Weight function has data length of %d rows.\n",
362 data_length);
363 printf("Reading the weight function into the program.\n");
364
365 // Initialise the weight vectors to correct dimensions
366 g_OPBinLimits = vector(data_length);
367 g_OPValues = vector(data_length - 1);
368 g_WValues = vector(data_length - 1);
369
370 // Read in the values. Note that g_OPBinLimits has one more entry than
371 // the weight vector.
372 if (W_file.is_open())
373 {
374 string line;
375 while (std::getline(W_file, line))
376 {
377 float bin_lim, weight, centre;
378 // Read lower bin limits and corresponding weights.
379 if (count >= 0 and count < data_length - 1)
380 {
381 sscanf(line.c_str(), "%e\t%e\t%e",
382 &bin_lim, &centre, &weight);
383
384 g_OPBinLimits[count] = bin_lim;
385 g_OPValues[count] = centre;
386 g_WValues[count] = weight;
387 }
388 // And the rightmost bin limit.
389 if (count == data_length - 1)
390 {
391 sscanf(line.c_str(), "%e", &bin_lim);
392 g_OPBinLimits[count] = bin_lim;
393 }
394 count += 1;
395 }
396
397 }
398 W_file.close();
399
400 // Fill out bin centres for the interpolator.
401 for (int i = 0; i < data_length - 1; i++)
402 {
403 g_OPValues[i] = (g_OPBinLimits[i + 1] + g_OPBinLimits[i]) / 2.0;
404 }
405 }
406 hila::out0 << "\nSuccesfully loaded the user provided weight function.\n";
407}
408
409////////////////////////////////////////////////////////////////////////////////
410/// @brief Reads the precomputed weight function from run_parameters struct
411/// and saves it into a file.
412/// @details The printing happens in an format identical to what is expected
413/// by the funciton read_weight_function. See its documentation for
414/// details.
415/// TBA: Add string input that can contain user specified header data.
416///
417/// @param W_function_filename
418/// @param g_WParam struct of weight iteration parameters
419////////////////////////////////////////////////////////////////////////////////
420void write_weight_function(string W_function_filename)
421{
422 if (hila::myrank() == 0)
423 {
424 std::ofstream W_file;
425 //string filename = generate_outfile_name(RP);
426 printf("Writing the current weight function into a file...\n");
427 W_file.open(W_function_filename.c_str());
428 //write_weight_file_header(W_file, FP, RP, g_WParam);
429
430 to_file(W_file, "%s", "OP_bin_limit\tOP_value\tWeight\n");
431 int i;
432 for (i = 0; i < g_OPValues.size(); ++i)
433 {
434 to_file(W_file, "%e\t", g_OPBinLimits[i]);
435 to_file(W_file, "%e\t", g_OPValues[i]);
436 to_file(W_file, "%e\n", g_WValues[i]);
437 }
438 // Remember to write the last bin upper limit
439 to_file(W_file, "%e\n", g_OPBinLimits[i]);
440 W_file.close();
441 printf("Succesfully saved the weight function into file\n%s\n",
442 W_function_filename.c_str());
443 }
444}
445
446////////////////////////////////////////////////////////////////////////////////
447/// @brief Returns a weight associated to the used order parameter.
448/// @details The function uses supplied pairs of points to linearly interpolate
449/// the function on the interval. This interpolant provides the
450/// requested weights to be used as the multicanonical weight.
451///
452/// @param OP value of the order parameter
453/// @return The value of the weight.
454////////////////////////////////////////////////////////////////////////////////
455double weight_function(double OP)
456{
457 double val;
458 // If out of range, constant extrapolation or for hard walls, num inf.
459 if (OP <= g_OPValues.front())
460 {
461 if (g_WParam.hard_walls)
462 val = std::numeric_limits<double>::infinity();
463 else
464 val = g_WValues.front();
465 }
466 else if (OP >= g_OPValues.back())
467 {
468 if (g_WParam.hard_walls)
469 val = std::numeric_limits<double>::infinity();
470 else
471 val = g_WValues.back();
472 }
473 // Otherwise find interval, calculate slope, base index, etc.
474 // Basic linear interpolation to obtain the weight value.
475 else
476 {
477 auto it = std::lower_bound(g_OPValues.begin(),
478 g_OPValues.end(), OP);
479 int i = std::distance(g_OPValues.begin(), it) - 1;
480 double y_value = g_WValues[i + 1] - g_WValues[i];
481 double x_value = g_OPValues[i + 1] - g_OPValues[i];
482 double slope = y_value / x_value;
483
484 double xbase = g_OPValues[i];
485 double ybase = g_WValues[i];
486
487 double xdiff = OP - xbase;
488 val = ybase + xdiff * slope;
489 }
490
491 return val;
492}
493
494////////////////////////////////////////////////////////////////////////////////
495/// @brief process 0 interface to "weight function" for the user accessing
496/// the weights.
497////////////////////////////////////////////////////////////////////////////////
498double weight(double OP)
499{
500 double val;
501 if (hila::myrank() == 0)
502 {
503 val = weight_function(OP);
504 }
505 hila::broadcast(val);
506 return val;
507}
508
509////////////////////////////////////////////////////////////////////////////////
510/// @brief Sets the static g_WeightIterationFlag to given boolean.
511///
512/// @param YN boolean indicating whether the iteration is to continue
513////////////////////////////////////////////////////////////////////////////////
515{
516 if (hila::myrank() == 0) g_WeightIterationFlag = YN;
517}
518
519////////////////////////////////////////////////////////////////////////////////
520/// @brief Returns the value of the static g_WeightIterationFlag to user
521///
522/// @return State of g_WeighITerationFlag
523////////////////////////////////////////////////////////////////////////////////
525{
526 bool flag;
527 if (hila::myrank() == 0) flag = g_WeightIterationFlag;
528 hila::broadcast(flag);
529 return flag;
530}
531
532////////////////////////////////////////////////////////////////////////////////
533/// @brief Accepts/rejects a multicanonical update.
534/// @details Using the values of the old and new order parameters the muca
535/// update is accepted with the logarithmic probability
536/// log(P) = - (W(OP_new) - W(OP_old))
537///
538/// @param OP_old current order parameter
539/// @param OP_new order parameter of proposed configuration
540/// @return Boolean indicating whether the update was accepted (true) or
541/// rejected (false).
542////////////////////////////////////////////////////////////////////////////////
543bool accept_reject(const double OP_old, const double OP_new)
544{
545 bool update;
546 bool AR_iterate;
547 // Only compute on node 0, broadcast to others
548 if (hila::myrank() == 0)
549 {
550 double W_new = weight_function(OP_new);
551 double W_old = weight_function(OP_old);
552
553 // get log(exp(-delta(W))) = -delta(W)
554 // (just like -delta(S) in Metropolis-Hastings)
555 double log_P = - (W_new - W_old);
556
557 // Get a random uniform from [0,1] and return a boolean indicating
558 // whether the update is to be accepted.
559 double rval = hila::random();
560 if (::log(rval) < log_P)
561 {
562 update = true;
563 }
564 else
565 {
566 update = false;
567 }
568
569 // Get value from process 0
570 AR_iterate = g_WParam.AR_iteration;
571 }
572
573 // Broadcast the update status to other processes along with the
574 // weight iteration parameter
576 hila::broadcast(AR_iterate);
577
578 // Check if iteration is enabled
579 if (AR_iterate)
580 {
581 if (update) set_weight_iter_flag(iterate_weights(OP_new));
582 else set_weight_iter_flag(iterate_weights(OP_old));
583 }
584
585 return update;
586}
587
588////////////////////////////////////////////////////////////////////////////////
589/// @brief Finds the index of the correc order parameter bin.
590/// @details Using the bin limit vector the correct order parameter bin is
591/// determined through a simple standard library search of
592/// g_OPBinLimits. When the value of the given order parameter is
593/// out of range, -1 is returned.
594///
595/// @param OP value of the order parameter
596/// @return integer index for the vector g_N_OP_Bin
597////////////////////////////////////////////////////////////////////////////////
598static int find_OP_bin_index(double OP)
599{
600 // Return -1 when not in the interval
601 if (OP <= g_OPBinLimits.front())
602 {
603 return -1;
604 }
605 else if (OP >= g_OPBinLimits.back())
606 {
607 return -1;
608 }
609 // Find index of minimum edge value such that edge < OP:
610 auto it = std::lower_bound(g_OPBinLimits.begin(),
611 g_OPBinLimits.end(), OP);
612 int lower_limit = std::distance(g_OPBinLimits.begin(), it) - 1;
613 return lower_limit;
614}
615
616////////////////////////////////////////////////////////////////////////////////
617/// @brief Same as find_OP_bin_index, except uses the index to simply modify the
618/// bin hit vector g_N_OP_Bin. Does not modify when outside of the range.
619/// @details
620///
621/// @param OP value of the order parameter
622////////////////////////////////////////////////////////////////////////////////
623static void bin_OP_value(double OP)
624{
625 // Don't bin visits outside of the binned areas
626 if (OP <= g_OPBinLimits.front())
627 {
628 return;
629 }
630 else if (OP >= g_OPBinLimits.back())
631 {
632 return;
633 }
634 // Find index of minimum edge value such that edge < OP:
635 auto it = std::lower_bound(g_OPBinLimits.begin(),
636 g_OPBinLimits.end(), OP);
637 int lower_limit = std::distance(g_OPBinLimits.begin(), it) - 1;
638 g_N_OP_Bin[lower_limit] += 1;
639}
640
641
642////////////////////////////////////////////////////////////////////////////////
643/// @brief Checks if all the bins have been visited by.
644/// @details Simply checks whether all bins have a nonzero number of entries
645///
646/// @param visit integer vector with values 1 corresponding to visits
647/// @return a boolean indicating the statement
648////////////////////////////////////////////////////////////////////////////////
649static bool all_visited(int_vector &n)
650{
651 int len = n.size();
652 for (int i = 0; i < len; ++i)
653 {
654 if (n[i] == 0) return false;
655 }
656 return true;
657}
658
659////////////////////////////////////////////////////////////////////////////////
660/// @brief Checks if the first and last bin have been visited
661/// @details Simply checks whether all bins have a nonzero number of entries.
662///
663/// @param visit integer vector with values 1 corresponding to visits
664/// @return a boolean indicating the statement
665////////////////////////////////////////////////////////////////////////////////
666static bool first_last_visited(int_vector &n)
667{
668 int len = n.size();
669 if ((n[0] == 0) or (n[len - 1] == 0))
670 return false;
671 else
672 return true;
673}
674
675////////////////////////////////////////////////////////////////////////////////
676/// @brief Sets a user provided function to the check in the "direct iteration"
677/// method.
678/// @details The for a given magnitude of update the "direct iteration" method
679/// periodically checks whether the MCMC chain has covered enough of the
680/// desired order parameter range, before reducing the update magnitude. Some
681/// preset methods exist (and should suffice) but when needed, a new condition
682/// can be set through this function. The input is a vector of integers
683/// indicating the number of visits to each bin, and the output is a boolean
684/// telling whether the desired condition has been achieved.
685///
686/// @param fc_pointer A function pointer to a suitable condition function
687////////////////////////////////////////////////////////////////////////////////
688void set_direct_iteration_FC(bool (* fc_pointer)(int_vector &n))
689{
690 finish_check = fc_pointer;
691}
692
693////////////////////////////////////////////////////////////////////////////////
694// Following three functions related to "canonical" iteration are currently
695// not implemented properly.
696////////////////////////////////////////////////////////////////////////////////
697/// @brief Computes an overcorrection of W given the visits ns (NOT FUNCTIONAL).
698/// @details Overcorrection checks which bins have been visited the most and
699/// modifies the weights in such a manner that the frequently visited
700/// bins are weighted (disproportionally) heavily. This should
701/// encourage the process to visit other bins during the next run of
702/// iterations.
703///
704/// @param Weight vector containing the previously used weights
705/// @param n_sum vector containing the total number of visits
706////////////////////////////////////////////////////////////////////////////////
707static void overcorrect(vector &Weight, int_vector &n_sum)
708{
709 int N = n_sum.size();
710 vector W(N, 0);
711 constexpr float C = 1;
712
713 for (int m = 0; m < N; ++m)
714 {
715 if (n_sum[m] > 0) W[m] = C * ::log(n_sum[m]);
716 }
717
718 // Redefine W[0] back to zero, set new weights
719 double base = W[0];
720 for (int i = 0; i < N; ++i)
721 {
722 Weight[i] += W[i] - base;
723 }
724}
725
726////////////////////////////////////////////////////////////////////////////////
727/// @brief Computes the next weight vector through iteration based on previous
728/// weights (NOT FUNCTIONAL).
729/// @details
730///
731/// @param Weight vector containing the previously used weights
732/// @param n vector containing the current number of visits
733/// @param g_sum vector containing the previous local sums of n
734/// @param g_log_h_sum vector containing the sum of previous logs of the
735/// canonical weights
736////////////////////////////////////////////////////////////////////////////////
737static void recursive_weight_iteration(vector &Weight, int_vector &n,
738 int_vector &g_sum, vector &g_log_h_sum)
739{
740 const int nmin = 10;
741 int N = n.size();
742
743 // Fill out log(h)
744 vector log_h(N);
745 for (int m = 0; m < N; ++m)
746 {
747 if (n[m] > 0)
748 {
749 log_h[m] = ::log(n[m]) + Weight[m];
750 }
751 // This is only going to get multiplied by zero,
752 // so the value doesn't matter.
753 else log_h[m] = 0;
754 }
755
756 // Compute the iteration. Note that W[0] is permanently kept to zero
757 vector W(N);
758 W[0] = 0;
759 Weight[0] = W[0];
760 for (int m = 1; m < N; ++m)
761 {
762 int gm;
763 // Check that both bins contain nmin hits before taking into account
764 if (n[m] > nmin and n[m - 1] > nmin)
765 {
766 gm = n[m] + n[m - 1];
767 }
768 else gm = 0;
769
770 g_sum[m] += gm;
771 g_log_h_sum[m] += gm * (log_h[m] - log_h[m - 1]);
772
773 W[m] = W[m - 1] + g_log_h_sum[m] / g_sum[m];
774
775 // Modify the input weights to their new values
776 Weight[m] = W[m];
777 }
778}
779
780//////////////////////////////////////////////////////////////////////////////////
781///// @brief Given an order parameter, iterates the weight function until the
782///// sampling becomes acceptably efficient. Save the weight function into
783///// a file for later use (NOT FUNCTIONAL).
784///// @details
785/////
786///// @param F struct of fields
787///// @param FP struct of field parameters
788///// @param RP struct of run parameters
789///// @param g_WParam struct of weight iteration parameters
790//////////////////////////////////////////////////////////////////////////////////
791//void iterate_weight_function_canonical(fields &F,
792// field_parameters &FP,
793// run_parameters &RP,
794// weight_iteration_parameters &g_WParam)
795//{
796// int samples = g_WParam.sample_size;
797// int n_sweeps = g_WParam.sample_steps;
798//
799// double max = g_WParam.max_OP;
800// double min = g_WParam.min_OP;
801//
802// int N = g_WParam.bin_number;
803// // Initialise the weight vector with the bin centres
804// // and get corresponding bin limits.
805// {
806// double dx = (max - min) / (N - 1);
807// for (int i = 0; i < N; ++i)
808// {
809// RP.OP_values[i] = min + i * dx;
810// }
811// }
812// vector limits = get_bin_limits(min, max, N);
813//
814// // Initialise the storage vectors to zero:
815// int_vector n(N, 0), g_sum(N, 0), n_sum(N, 0);
816// vector g_log_h_sum(N, 0), log_h(N, 0), W_prev(N, 0);
817//
818// // Get initial guesses
819// for (int i = 0; i < N; ++i)
820// {
821// n[i] = 0;
822// g_sum[i] = g_WParam.initial_bin_hits;
823// log_h[i] = RP.W_values[i];
824// }
825//
826// static int count = 1;
827// while (true)
828// {
829// float accept = 0;
830// float OP_sum = 0;
831// for (int i = 0; i < samples; i++)
832// {
833// accept += mc_update_sweeps(F, FP, RP, n_sweeps);
834// OP_sum += FP.OP_value;
835// bin_OP_values(n, limits, FP.OP_value);
836// }
837//
838// for (int m = 0; m < N; m++)
839// {
840// n_sum[m] += n[m];
841// }
842//
843// if (count % g_WParam.OC_frequency == 0 and count < g_WParam.OC_max_iter)
844// {
845// overcorrect(RP.W_values, n_sum);
846// }
847// else
848// {
849// recursive_weight_iteration(RP.W_values, n, g_sum, g_log_h_sum);
850// }
851//
852//
853// // Some mildly useful print out
854// int nmax = *std::max_element(n_sum.begin(), n_sum.end());
855// for (int m = 0; m < N; ++m)
856// {
857// std::string n_sum_log = "";
858// for (int i = 0; i < int(n_sum[m] * 50.0 / nmax); i++)
859// {
860// n_sum_log += "|";
861// }
862// if (hila::myrank() == 0)
863// {
864// printf("%5.3f\t%10.3f\t\t%d\t%s\n", limits[m],
865// RP.W_values[m],
866// n_sum[m], n_sum_log.c_str());
867// }
868// n[m] = 0;
869// }
870//
871// count += 1;
872// if (count > g_WParam.max_iter) break;
873// }
874//}
875
876////////////////////////////////////////////////////////////////////////////////
877/// @brief Procures a vector containing equidistant bin edges.
878/// @details
879///
880/// @return vector containing the bin edges
881////////////////////////////////////////////////////////////////////////////////
882static vector get_equidistant_bin_limits()
883{
884 double min = g_WParam.min_OP;
885 double max = g_WParam.max_OP;
886 int N = g_WParam.bin_number;
887 vector bin_edges(N + 1);
888 double diff = (max - min) / (N - 1);
889 for (int i = 0; i < N + 1; ++i)
890 {
891 bin_edges[i] = min - diff / 2.0 + diff * i;
892 }
893 return bin_edges;
894}
895
896////////////////////////////////////////////////////////////////////////////////
897/// @brief Sets up the global vectors for bin limits and centres using
898/// get_equidistant_bin_limits.
899/// @details
900////////////////////////////////////////////////////////////////////////////////
901static void setup_equidistant_bins()
902{
903 // Get bin limits so that centre of first bin is at min_OP and
904 // the last bin centre is at max_OP.
905 g_OPBinLimits = get_equidistant_bin_limits();
906 for (int i = 0; i < g_OPValues.size(); i++)
907 {
908 double centre = (g_OPBinLimits[i + 1] + g_OPBinLimits[i]) / 2.0;
909 g_OPValues[i] = centre;
910 }
911}
912
913////////////////////////////////////////////////////////////////////////////////
914/// @brief Initialises the global vectors appropriately, setting up a binning
915/// if not provided by the user.
916/// @details The global vectors are initialised to correct dimensions as to
917/// prevent indexing errors in the iteration methods.
918////////////////////////////////////////////////////////////////////////////////
919static void initialise_weight_vectors()
920{
921 // If no input weight, set up equidistant bins
922 if (g_WParam.weight_loc.compare("NONE") == 0)
923 {
924 int N = g_WParam.bin_number;
925 g_WValues = vector(N, 0.0);
926 g_OPValues = vector(N, 0.0);
927 g_OPBinLimits = vector(N + 1, 0.0);
928
929 setup_equidistant_bins();
930
931 g_N_OP_Bin = int_vector(N, 0);
932 g_N_OP_BinTotal = int_vector(N, 0);
933 }
934 // Same for predetermined bins. g_OPValues, g_WValues
935 // and g_OP_BinLimits have been read from the input file.
936 // To prevent any accidents with the iterators, these bin vectors
937 // are initialised in all cases. Should really not affect performance.
938 else
939 {
940 int N = g_WValues.size();
941 g_N_OP_Bin = int_vector(N, 0);
942 g_N_OP_BinTotal = int_vector(N, 0);
943 }
944}
945
946////////////////////////////////////////////////////////////////////////////////
947/// @brief Given an order parameter, bins it to correct weight interval, and
948/// periodically updates the weights accordingly.
949/// @details This extremely simple update method
950///
951/// @param OP order parameter of the current configuration (user supplied)
952/// @return boolean indicating whether the iteration is considered complete
953////////////////////////////////////////////////////////////////////////////////
954static bool iterate_weight_function_direct(double OP)
955{
956 bool continue_iteration;
957 if (hila::myrank() == 0)
958 {
959 int samples = g_WParam.DIP.sample_size;
960 int N = g_WValues.size();
961
962 bin_OP_value(OP);
963 g_WeightIterationCount += 1;
964
965 if (g_WeightIterationCount >= samples)
966 {
967 for (int m = 0; m < N; m++)
968 {
969 g_WValues[m] += g_WParam.DIP.C * g_N_OP_Bin[m] * N / samples;
970 g_N_OP_BinTotal[m] += g_N_OP_Bin[m];
971 }
972
973 if (g_WParam.visuals) print_iteration_histogram();
974
975 double base = *std::min_element(g_WValues.begin(), g_WValues.end());
976 for (int m = 0; m < N; ++m)
977 {
978 // Always set minimum weight to zero. This is inconsequential
979 // as only the differences matter.
980 g_WValues[m] -= base;
981 g_N_OP_Bin[m] = 0;
982 }
983 g_WeightIterationCount = 0;
984
985 if (finish_check(g_N_OP_BinTotal))
986 {
987 for (int m = 0; m < N; m++)
988 {
989 g_N_OP_BinTotal[m] = 0;
990 }
991
992 g_WParam.DIP.C /= 1.5;
993 hila::out0 << "Decreasing update size...\n";
994 hila::out0 << "New update size C = " << g_WParam.DIP.C << "\n\n";
995 }
996 write_weight_function("intermediate_weight.dat");
997 hila::out0 << "Update size C = " << g_WParam.DIP.C << "\n\n";
998 }
999
1000 continue_iteration = true;
1001 if (g_WParam.DIP.C < g_WParam.DIP.C_min)
1002 {
1003 hila::out0 << "Reached minimum update size.\n";
1004 hila::out0 << "Weight iteration complete.\n";
1005 continue_iteration = false;
1006 }
1007 }
1008 hila::broadcast(continue_iteration);
1009 return continue_iteration;
1010}
1011
1012////////////////////////////////////////////////////////////////////////////////
1013/// @brief Same as iterate_weight_function_direct for sample size 1.
1014/// @details In this special case the weights are modified after each new
1015/// value of the order parameter. Reduced internal complexity due to
1016/// this simplification.
1017/// Whether all bins have been visited is now checked only every
1018/// g_WParam.DIP.single_check_interval to prevent excessive checking
1019/// for the visits.
1020///
1021/// @param OP order parameter of the current configuration (user supplied)
1022/// @return boolean indicating whether the iteration is considered complete
1023////////////////////////////////////////////////////////////////////////////////
1024static bool iterate_weight_function_direct_single(double OP)
1025{
1026 int continue_iteration;
1027 if (hila::myrank() == 0)
1028 {
1029 int samples = g_WParam.DIP.sample_size;
1030 int N = g_WValues.size();
1031
1032 int bin_index = find_OP_bin_index(OP);
1033 // Only increment if on the min-max interval
1034 if (bin_index != -1)
1035 g_N_OP_BinTotal[bin_index] += 1;
1036
1037 g_WeightIterationCount += 1;
1038
1039 g_WValues[bin_index] += g_WParam.DIP.C;
1040
1041 if (g_WeightIterationCount % g_WParam.DIP.single_check_interval == 0)
1042 {
1043
1044 double base = *std::min_element(g_WValues.begin(), g_WValues.end());
1045 for (int m = 0; m < N; ++m)
1046 {
1047 // Always set minimum weight to zero. This is inconsequential
1048 // as only the differences matter.
1049 g_WValues[m] -= base;
1050 g_N_OP_Bin[m] = 0;
1051 }
1052
1053 // Visuals
1054 if (g_WParam.visuals) print_iteration_histogram();
1055
1056 // If condition satisfied, zero the totals and decrease C
1057 if (finish_check(g_N_OP_BinTotal))
1058 {
1059 for (int m = 0; m < N; m++)
1060 {
1061 g_N_OP_BinTotal[m] = 0;
1062 }
1063
1064 g_WParam.DIP.C /= 1.5;
1065 hila::out0 << "Decreasing update size...\n";
1066 hila::out0 << "New update size C = " << g_WParam.DIP.C << "\n\n";
1067 }
1068
1069 continue_iteration = true;
1070 if (g_WParam.DIP.C < g_WParam.DIP.C_min)
1071 {
1072 hila::out0 << "Reached minimum update size.\n";
1073 hila::out0 << "Weight iteration complete.\n";
1074 continue_iteration = false;
1075 }
1076
1077 write_weight_function("intermediate_weight.dat");
1078 hila::out0 << "Update size C = " << g_WParam.DIP.C << "\n\n";
1079 }
1080 }
1081 hila::broadcast(continue_iteration);
1082 return continue_iteration;
1083}
1084
1085////////////////////////////////////////////////////////////////////////////////
1086/// @brief Prints out a crude horisontal histogram.
1087/// @details Procures a crude horisontal ASCII histogram based on the g_N_OP_Bin
1088/// vector. The histogram bin heights are proportional to g_N_OP_Bin
1089/// values. This is not very expressive for large N, as it won't fit
1090/// the height of the screen.
1091////////////////////////////////////////////////////////////////////////////////
1092static void print_iteration_histogram()
1093{
1094 int samples = g_WParam.DIP.sample_size;
1095 int N = g_WValues.size();
1096 // Find maximum bin content for normalisation
1097 int nmax = *std::max_element(g_N_OP_Bin.begin(), g_N_OP_Bin.end());
1098 // Write a column header
1099 printf("Order Parameter Weight Number of hits\n");
1100
1101 for (int m = 0; m < N; ++m)
1102 {
1103 // For each bin get a number of "|":s proportional to the number of
1104 // hits to each bin and print it out along with relevant numerical
1105 // values
1106 std::string n_sum_hist = "";
1107 if (g_N_OP_BinTotal[m] > 0) n_sum_hist += "O";
1108 for (int i = 0; i < int(g_N_OP_Bin[m] * 200.0 / samples); i++)
1109 {
1110 n_sum_hist += "|";
1111 }
1112 printf("%-20.3f%-20.3f%d\t\t\t%s\n", g_OPValues[m],
1113 g_WValues[m], g_N_OP_Bin[m], n_sum_hist.c_str());
1114 }
1115}
1116
1117////////////////////////////////////////////////////////////////////////////////
1118/// @brief Initialises all the variables needed for the weight iteration.
1119/// @details For the iteration the function pointer to iterate_weights must be
1120/// initialised. The condition for proceeding to the next step of the
1121/// iteration is also set through a function pointer.
1122///
1123/// The correct methods are chosen according to the
1124/// parameter file. Further, method specific variables that are
1125/// modified during the run are also set to the necessary values.
1126////////////////////////////////////////////////////////////////////////////////
1127static void setup_iteration()
1128{
1129 // Initialise iterate_weights by pointing it at the
1130 // correct method
1131 if (g_WParam.method.compare("direct") == 0)
1132 {
1133 if (g_WParam.DIP.sample_size > 1)
1134 {
1135 iterate_weights = &iterate_weight_function_direct;
1136 }
1137 else
1138 {
1139 iterate_weights = &iterate_weight_function_direct_single;
1140 }
1141 g_WParam.DIP.C = g_WParam.DIP.C_init;
1142 }
1143 else
1144 {
1145 iterate_weights = &iterate_weight_function_direct;
1146 g_WParam.DIP.C = g_WParam.DIP.C_init;
1147 }
1148
1149 // Zero the iteration counter
1150 g_WeightIterationCount = 0;
1151
1152 // Set up the finish condition pointer for the direct method.
1153 if (g_WParam.DIP.finish_condition.compare("all_visited") == 0)
1154 {
1155 finish_check = &all_visited;
1156 }
1157 else if (g_WParam.DIP.finish_condition.compare("ends_visited") == 0)
1158 {
1159 finish_check = &first_last_visited;
1160 }
1161 else
1162 {
1163 finish_check = &all_visited;
1164 }
1165}
1166
1167////////////////////////////////////////////////////////////////////////////////
1168/// @brief Enable/disable continuous weight iteration
1169/// @details Premits the user to enable/disable continuous weight iteration at
1170/// each call to accept_reject. Simply modifies a flag parameter
1171/// that is checked in accept_reject.
1172///
1173/// @param YN enable (true) or disable (false) the iteration
1174////////////////////////////////////////////////////////////////////////////////
1176{
1177 if (hila::myrank() == 0) g_WParam.AR_iteration = YN;
1178}
1179
1180////////////////////////////////////////////////////////////////////////////////
1181/// @brief Loads parameters and weights for the multicanonical computation.
1182/// @details Sets up iteration variables. Can be called multiple times and must
1183/// be called at least once before attempting to use any of the muca
1184/// methods.
1185///
1186/// @param wfile_name path to the weight parameter file
1187////////////////////////////////////////////////////////////////////////////////
1188void initialise(const string wfile_name)
1189{
1190 // Read parameters into g_WParam struct
1191 read_weight_parameters(wfile_name);
1192 // This is fine to do just for process zer0
1193 if (hila::myrank() == 0)
1194 {
1195 // Read pre-existing weight if given
1196 if (g_WParam.weight_loc.compare("NONE") != 0)
1197 {
1199 }
1200
1201 // Initialise rest of the uninitialised vectors
1202 initialise_weight_vectors();
1203 }
1204
1205 // Choose an iteration method (or the default)
1206 setup_iteration();
1207}
1208
1209}
1210}
hila::input - Class for parsing runtime parameter files.
Definition input.h:52
void close()
Closes input parameter file.
Definition input.cpp:79
returntype get(const std::string &key)
Get next value in stack of read in input string from parameters file.
Definition input.h:269
void set_continuous_iteration(bool YN)
Enable/disable continuous weight iteration.
void write_weight_function(string W_function_filename)
Reads the precomputed weight function from run_parameters struct and saves it into a file.
void initialise(const string wfile_name)
Loads parameters and weights for the multicanonical computation.
bool accept_reject(const double OP_old, const double OP_new)
Accepts/rejects a multicanonical update.
void to_file(std::ofstream &output_file, string fmt, K input_value)
Writes a variable to the file, given the format string.
void read_weight_function(string W_function_filename)
Reads a precomputed weight function from file.
void set_weight_iter_flag(bool YN)
Sets the static g_WeightIterationFlag to given boolean.
double weight_function(double OP)
Returns a weight associated to the used order parameter.
double weight(double OP)
process 0 interface to "weight function" for the user accessing the weights.
void read_weight_parameters(string parameter_file_name)
Parses the weight parameter file and fills the g_WParam struct.
bool check_weight_iter_flag()
Returns the value of the static g_WeightIterationFlag to user.
string generate_outfile_name()
Generates a time stamped and otherwise appropriate file name for the saved weight function files.
void set_direct_iteration_FC(bool(*fc_pointer)(int_vector &n))
Sets a user provided function to the check in the "direct iteration" method.
Header for model agnostic implementation of various multicanonical (muca) methods.
Implement hila::swap for gauge fields.
Definition array.h:981
logger_class log
Now declare the logger.
int myrank()
rank of this node
Definition com_mpi.cpp:235
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:118
std::ostream out0
This writes output only from main process (node 0)
std::ofstream output_file
this is just a hook to store output file, if it is in use
Definition initialize.cpp:8
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:153
An internal struct parametrising the canonical weight iteration method.
int initial_bin_hits
Initial number of entries in the bin totals.
int sample_size
Number of samples before weight function update.
int OC_max_iter
Up to which iteration the overcorrection updates can be used.
int OC_frequency
How often the overcorrection update is used.
An internal struct parametrising the direct weight iteration method.
double C
Magnitude of the weight update.
int single_check_interval
Determines how often the update condition is checked for the case direct_iteration::sample_size = 1.
string finish_condition
Determines the iteration condition for the iteration method.
double C_min
Minimum magnitude of the weight update.
int sample_size
Number of samples before weight function update.
double C_init
Initial magnitude of the weight update.
An internal struct parametrising multicanonical methods.
string weight_loc
Path to the weight function file.
double min_OP
Minimum order parameter value.
double max_OP
Maximum order parameter value.
bool hard_walls
Whether the weight outside max_OP and min_OP is infinite.
string method
Name of the iteration method.
int bin_number
Number of OP bins.
struct canonical_iteration CIP
A substruct that contains method specific parameters.
struct direct_iteration DIP
A substruct that contains method specific parameters.
bool AR_iteration
Whether to update the weights after each call to accept_reject.
bool visuals
Whether to print histogram during iteration.
string outfile_name_base
Prefix for the saved weight function files.
void update(GaugeField< group > &U, const parameters &p, bool relax)
Wrapper update function.
Definition suN_gauge.cpp:73