5 This module provides a few methods to improve the efficiency of a
6 replica-exchange simulation by tuning its parameters.
10 import rpy2.robjects
as robjects
12 kB = 1.3806503 * 6.0221415 / 4184.0
26 robjects.globalenv[
"kB"] = kB
27 _rinverf = r(
'invErf <- function(x) {qnorm((1 + x) /2) / sqrt(2)}')
28 _rerf = r(
'erf <- function(x) {2 * pnorm(x * sqrt(2)) - 1}')
42 return _rinvF(x, d1, d2)[0]
45 def spline(xy, mean, method=None):
46 """spline interpolation of (x,y) coordinates. If interpolation goes
47 negative, replace by mean value.
50 robjects.globalenv[
"x"] = robjects.FloatVector(x)
51 robjects.globalenv[
"y"] = robjects.FloatVector(y)
55 r(
'cvsplinenonbounded <- splinefun(x,y)')
57 r(
'cvsplinenonbounded <- splinefun(x,y,method="%s")' % method)
59 'cvspline <- function(x) { tmp = cvsplinenonbounded(x); '
60 'if (tmp>0) {tmp} else {%f}}' %
69 def linear_interpolation(xy, mean):
70 """linear interpolation of (x,y) coordinates. No extrapolation possible.
73 robjects.globalenv[
"x"] = robjects.FloatVector(x)
74 robjects.globalenv[
"y"] = robjects.FloatVector(y)
77 _rinterp = r(
'cvspline <- approxfun(x,y)')
88 """perform anova using R and return statistic, p-value, between and
93 reps = r.rep(0, len(args[0]))
94 weight = robjects.FloatVector(args[0])
95 for i
in range(1, len(args)):
96 reps += r.rep(i, len(args[i]))
97 weight += robjects.FloatVector(args[i])
98 group = r.factor(reps)
99 robjects.globalenv[
"weight"] = weight
100 robjects.globalenv[
"group"] = group
101 lm = r.lm(
"weight ~ group")
105 anova_result = {
'fstat': aov[3][0],
107 'between': aov[2][0],
109 'nsteps': [len(i)
for i
in args],
116 """perform kruskal-wallis rank test"""
120 reps = r.rep(0, len(args[0]))
121 weight = robjects.FloatVector(args[0])
122 for i
in range(1, len(args)):
123 reps += r.rep(i, len(args[i]))
124 weight += robjects.FloatVector(args[i])
125 group = r.factor(reps)
126 aov = r(
'kruskal.test')(group, weight)
128 kruskal_result = {
'fstat': aov[0][0],
130 'nsteps': [len(i)
for i
in args],
133 return kruskal_result
136 def ttest(obs, target):
137 """perform a one-sample two-sided t-test on obs against target mean"""
138 test = r(
't.test')(robjects.IntVector(obs), mu=target)
139 return test[0][0], test[2][0]
142 def binom(obs, target):
143 """perform an exact binomial test on the mean of obs against target"""
146 test = r(
'binom.test')(success, trials, p=target)
147 return test[0][0], test[2][0]
151 """perform bartlett's test on the equality of variances of the
155 group = r.gl(ngroups, nreps)
156 weight = robjects.IntVector(args[0])
158 weight += robjects.IntVector(i)
159 robjects.globalenv[
"weight"] = weight
160 robjects.globalenv[
"group"] = group
161 var = r(
'bartlett.test')(weight, group)
162 return var[0][0], var[2][0]
166 """perform Fligner-Killeen non-parametric test of the variance equality"""
169 group = r.gl(ngroups, nreps)
170 weight = robjects.IntVector(args[0])
172 weight += robjects.IntVector(i)
173 robjects.globalenv[
"weight"] = weight
174 robjects.globalenv[
"group"] = group
175 var = r(
'fligner.test')(weight, group)
176 return var[0][0], var[2][0]
179 def power_test(ar, power=0.8, alpha=0.05):
180 """perform an anova power test and return
181 - the power of the test with this input data
182 - the number of points that would be needed to achieve a default
184 ar: the output of anova()
186 result = r(
'power.anova.test')(groups=ar[
'nreps'], n=min(ar[
'nsteps']),
187 between=ar[
'between'], within=ar[
'within'],
189 prdb(
'the power of this anova was: %.3f' % result[5][0])
190 result = r(
'power.anova.test')(groups=ar[
'nreps'],
191 between=ar[
'between'], within=ar[
'within'],
192 sig=alpha, pow=power)
193 prdb(
'To have a power of %.3f, there should be at least %d exchange '
194 'attempts.' % (power, result[1][0]))
198 def minimum_n(ar, alpha=0.05):
199 """This routine tries to return an estimate of the additional number of
200 exchange trials that could lead to a positive result of the anova (e.g.
201 the average ARs are not the same). It is still very crude. It also assumes
202 that a one-way anova was made.
203 ar: the output of anova()
207 nsteps = ar[
'nsteps']
213 return nsteps * (numpy.sqrt(Finv(1 - alpha, nreps - 1,
214 nreps * (nsteps - 1)) / fstat) - 1)
220 """When created, estimates the heat capacity from the energies or from the
221 indicator functions using the specified method. Two methods are then
222 available to guess the heat capacity at a given parameter value: get()
223 returns a single point, and mean() returns the linear average between two
227 def __init__(self, params, energies=None, indicators=None,
228 method=
"constant", temps=
None, write_cv=
False):
230 self.__initialized =
False
234 if method ==
"interpolate":
238 elif method ==
"constant":
240 self.get =
lambda p: self.__cv
241 self.mean =
lambda p1, p2: self.__cv
242 self.__cvfun = self.get
243 r(
'cvspline <- function(x) {%f}' % self.__cv)
244 elif method ==
"mbar":
247 self.mean = self.mean_mbar
249 raise NotImplementedError(method)
251 self.__initialized =
True
256 fl.write(
"".join([
"%f %f\n" % (x, self.get(x))
257 for x
in numpy.linspace(params[0] / 2, 2 * params[-1])]))
261 """interpolate using previous values, by reversing the approximate
264 if self.__initialized:
266 if len(indicators) != len(params) - 1:
268 "the length of indicators and params does not match!")
269 if params != tuple(sorted(params)):
270 raise NotImplementedError(
271 "unable to work on parameters that do not change "
274 prdb(
"storing params and means")
275 self.__params = params
276 self.__pmeans = [(params[i] + params[i + 1]) / 2.
277 for i
in range(len(params) - 1)]
278 prdb(
"computing __cv")
279 for i, ind
in enumerate(indicators):
280 mean = sum(ind) / float(len(ind))
281 Y2 = 2 * kB * erfinv(1 - mean) ** 2
286 (p1 ** 2 + p2 ** 2) * float(Y2) / (p2 - p1) ** 2))
289 self.__cvmean = sum([i[1]
for i
in self.__cv]) / float(len(self.__cv))
290 if self.__cvmean < 0:
291 raise ValueError(
"Cv mean is negative!")
292 self.__cvfun = spline(self.__cv, self.__cvmean)
296 """try to guess which constant cv fits best"""
297 if self.__initialized:
300 self.__cv = self.__cvmean
303 def needs_init(self):
304 if not self.__initialized:
305 raise RuntimeError(
"Class was not initialized correctly!")
308 "use MBAR to get the heat capacity"
309 raise NotImplementedError(
"estimate_cv_mbar")
311 def _isinbounds(self, p, params):
312 """returns True if p is within params, else false. the params list
313 must be sorted ascendingly."""
314 if p < params[0] - EPSILON
or p > params[-1] + EPSILON:
321 def _interpolate(self, xval, xlist):
322 """return interpolation of Cv at point xval, and return the average
323 instead if this value is negative.
325 self._isinbounds(xval, xlist)
326 val = self.__cvfun(xval)
333 """returns the point estimate of the first derivative of the energy
334 with respect to the replica exchange parameter (usually T or q).
335 This version assumes that the means of cv are given.
338 return self._interpolate(param, self.__pmeans)
341 """returns the point estimate of the first derivative of the energy
342 with respect to the replica exchange parameter (usually T or q).
343 This version assumes that the values of cv are given.
346 return self._interpolate(param, self.__params)
349 """estimate the mean of Cv between two points. Here the means were
353 return self._interpolate((pa + pb) / 2., self.__pmeans)
355 def mean_mbar(self, pa, pb):
362 def update_good_dumb(newp, oldp, *args, **kwargs):
363 """Here the old parameters are oldp[0] and oldp[1], and the starting point
364 is newp[0]. We should modify newp[1] so that the AR in the following cycle
365 is equal to the targetAR.
366 In the "dumb" method, the Cv and targetAR keywords are ignored.
367 Here the newp[1] parameter is modified because prior changes have set
368 newp[0] to a different value than oldp[0]. Thus, we should move newp[1] by
369 minimizing the effect on the AR since it is supposedly equal to targetAR.
370 In this simple method, the parameter is just translated.
373 "newp[0] has moved (%.3f -> %.3f), adjusting the position of newp[1]" %
375 return oldp[1] - (oldp[0] - newp[0])
378 def update_bad_dumb(newp, oldp, ind, targetAR=0.4, scale=0.1, **kwargs):
379 """Here the old parameters are oldp[0] and oldp[1], and the starting point
380 is newp[0]. We should modify newp[1] so that the AR in the following cycle
381 is equal to the targetAR.
382 In the "dumb" method, the Cv keyword is ignored. Here the newp[1]
383 parameter is modified to follow the possible translation of newp[0] by
384 calling update_good_dumb, and then newp[1] is added or subtracted scale% of
385 (oldp[1] - oldp[0]) to adjust to targetAR.
388 if newp[0] != oldp[0]:
389 newp[1] = update_good_dumb(newp, oldp)
390 if targetAR > sum(ind) / float(len(ind)):
391 prdb(
"""target AR is higher than expected, decreasing newp[1]""")
392 newp[1] -= scale * (oldp[1] - oldp[0])
394 prdb(
"""target AR is lower than expected, increasing newp[1]""")
395 newp[1] += scale * (oldp[1] - oldp[0])
399 def update_any_cv_step(newp, oldp, ind, targetAR=0.4, Cv=None, **kwargs):
400 """here we use the average AR formula of two gaussians to get newp[1] as a
401 function of newp[1], knowing the targetAR and estimating the Cv. If
402 targetAR is negative, consider that mean(ind) equals the target AR and
403 skip any calculation in the case that oldp[0] equals newp[0].
404 step: suppose the heat capacity is stepwise constant, i.e. use the heat
405 capacity at position newp[0] as an estimate of the mean of the heat
406 capacity between newp[0] and newp[1]. This does not require any
407 self-consistent loop.
412 if abs(oldp[0] - newp[0]) < EPSILON
and targetAR < 0:
415 targetAR = sum(ind) / float(len(ind))
417 Y = numpy.sqrt(2 * kB) * float(erfinv(1 - targetAR))
419 raise ValueError(
"""targetAR too small for this approximate method, use
420 the full self-consistent method instead.""")
421 return newp[0] * (cv + Y * numpy.sqrt(2 * cv - Y ** 2)) / (cv - Y ** 2)
424 def update_any_cv_sc(newp, oldp, ind, targetAR=0.4, Cv=None,
425 tol=1e-6, maxiter=10000):
426 """self-consistent solver version"""
430 if abs(oldp[0] - newp[0]) < EPSILON
and targetAR < 0:
433 targetAR = sum(ind) / float(len(ind))
435 Y = numpy.sqrt(2 * kB) * float(erfinv(1 - targetAR))
437 raise ValueError(
"""targetAR too small for this approximate method, use
438 the full self-consistent method instead.""")
439 targetp = newp[0] * (cv + Y * numpy.sqrt(2 * cv - Y ** 2)) / (cv - Y ** 2)
440 for i
in range(maxiter):
441 cv = Cv.mean(newp[0], targetp)
442 (oldtargetp, targetp) = (
443 targetp, newp[0] * (cv + Y * numpy.sqrt(2 * cv - Y ** 2))
445 if abs(targetp - oldtargetp) <= tol:
447 if numpy.isnan(targetp):
450 "targetAR too small for this approximate method, use the "
451 "full self-consistent method instead.")
453 raise ValueError(
"""something unexpected happened""")
455 prdb(
"""Warning: unable to converge the self-consistent after %d
456 iterations and a tolerance of %f. Change the method or decrease the
457 tolerance!""" % (maxiter, tol))
458 prdb(
"converged after %d iterations and a tolerance of %f for x=%f" %
463 def update_any_cv_scfull(newp, oldp, ind, targetAR=0.4, Cv=None,
464 tol=1e-6, maxiter=10000):
465 """self-consistent solver version, on the exact average AR equation"""
468 _ = r(
'u21 <- function(t1,t2) { integrate(Vectorize(cvspline),'
470 _ = r(
'b21 <- function(t1,t2) { 1./(kB*t2) - 1./(kB*t1) }')
471 _ = r(
'sigma2 <- function(t) {cvspline(t)*kB*t**2}')
472 _rovboltz = r(
'ovboltz <- function(t1,t2) {\
474 u21(t1,t2)/sqrt(2*(sigma2(t1)+sigma2(t2))))\
475 + exp(b21(t1,t2)*(u21(t1,t2)+b21(t1,t2)*(sigma2(t1)+sigma2(t2))/2))\
476 * (1+erf((u21(t1,t2)+b21(t1,t2)*(sigma2(t1)+sigma2(t2)))\
477 /(sqrt(2*(sigma2(t1)+sigma2(t2))))))\
480 'rootfn <- function(t2) {ovboltz(%f,t2)-%f}' %
484 if oldp[1] > oldp[0]:
489 while _rrootfn(tmp)[0] >= 0:
491 tmp += (oldp[1] - oldp[0])
493 raise RuntimeError(
"heat capacity goes negative")
495 raise RuntimeError(
'could not find zero of function!')
498 _runiroot = r(
'uniroot(rootfn,c(%f,%f),f.lower = %f, f.upper = %f, '
499 'tol = %f, maxiter = %d)'
500 % (newp[0], tmp, 1 - targetAR, -targetAR, tol, maxiter))
501 prdb(
"self-consistent solver converged after %s iterations and an "
502 "estimated precision of %s " % (_runiroot[2][0], _runiroot[3][0]))
508 _runiroot[0][0])[0]])
509 return _runiroot[0][0]
512 def update_any_cv_nr(newp, oldp, ind, targetAR=0.4, Cv=None, **kwargs):
513 """newton-raphson solver version"""
516 raise NotImplementedError
521 def are_equal_to_targetAR(
526 """here, all indicators have same average, we want to know if it is
531 deviations = sorted([(abs(sum(ind) / float(len(ind)) - targetAR), ind)
532 for pos, ind
in enumerate(indicators)])
533 deviant = deviations[-1]
536 if method ==
"ttest":
539 elif method ==
"binom":
542 raise NotImplementedError
545 test, pval = our_ttest(deviant[1], targetAR)
547 if abs(targetAR - sum(deviant[1]) / len(deviant[1])) > EPSILON:
557 def are_stationnary(indicators, alpha=0.05, method="anova"):
558 """test on the stationarity of the observations (block analysis). Done
559 so by launching an anova on the difference between the two halves of
563 if method ==
"kruskal":
568 tmp = numpy.array(indicators)
569 blocklen = len(indicators[0]) / 2
570 block = tmp[:, :blocklen] - tmp[:, blocklen:2 * blocklen]
571 if test(*block)[
'pval'] < alpha:
577 def are_equal(indicators, targetAR=0.4, alpha=0.05,
578 method=
"anova", varMethod=
"skip", power=0.8):
579 """Perform a one-way ANOVA or kruskal-wallis test on the indicators set,
580 and return True if one cannot exclude with risk alpha that the indicators
581 AR are different (i.e. True = all means are equal). Also performs a test
582 on the variance (they must be equal).
585 if min(targetAR, 1 - targetAR) * len(indicators[0]) <= 5
and \
586 (varMethod ==
"bartlett" or method ==
"anova"):
587 prdb(
"Warning: normal approximation to the binomial does not hold!")
590 if varMethod ==
"skip":
593 if varMethod ==
"bartlett":
594 pval = bartlett(*indicators)[1]
595 elif varMethod ==
"fligner":
596 pval = fligner(*indicators)[1]
598 raise NotImplementedError(
599 "variance testing method unknown: %s" %
602 prdb(
"Warning: performing mean test with unequal variances.")
604 if method ==
"kruskal":
609 tr = test(*indicators)
612 tr[
'result'] = tr[
'pval'] >= alpha
617 def find_good_ARs(indicators, targetAR=0.4, alpha=0.05, method="binom"):
618 """perform one-sample t-tests on each of the data sets, and return
619 a tuple of bool of size N-1, False if AR i is not equal to targetAR
624 means = sorted([(sum(ind) / float(len(ind)), pos, ind)
625 for pos, ind
in enumerate(indicators)])
628 if method ==
"ttest":
631 elif method ==
"binom":
634 raise NotImplementedError
638 prdb(
"starting left")
639 for (i, (mean, pos, ind))
in enumerate(means):
640 prdb(
"performing t-test on couple %d having average AR %f, "
641 "position %d" % (pos, mean, i))
643 test, pval = our_ttest(ind, targetAR)
645 if abs(targetAR - mean) > EPSILON:
651 isGoodTuple.append((pos,
False))
656 prdb(
"starting right")
657 for (i, (mean, pos, ind))
in enumerate(reversed(means)):
658 prdb(
"performing t-test on couple %d having average AR %f, position %d"
659 % (pos, mean, len(means) - 1 - i))
660 if our_ttest(ind, targetAR)[1] < alpha:
662 isGoodTuple.append((pos,
False))
664 goodstop = len(means) - 1 - i
668 if len(isGoodTuple) > len(indicators):
669 return tuple([
False] * len(indicators))
671 elif len(isGoodTuple) == 0:
672 return tuple([
True] * len(indicators))
675 isGoodTuple.extend([(means[i][1],
True)
for i
in
676 range(goodstart, goodstop + 1)])
678 return tuple([tup[1]
for tup
in isGoodTuple])
683 def mean_first_passage_times(
688 """compute mean first passage times as suggested in
689 Nadler W, Meinke J, Hansmann UHE, Phys Rev E *78* 061905 (2008)
691 use_avgAR : if a list of average ARs is given computes everything from
692 average AR; if it is False, compute by direct counting of events.
695 If use_avgAR == False:
696 tau0, tauN, chose_N, times0, timesN
698 tau0, tauN, None, None, None
699 tau0[i]: average time to go from replica 0 to replica i
700 tauN[i]: average time to go from replica N to replica i
701 times0 and timesN are the corresponding lists of single events.
705 from numpy
import array, zeros
706 replicanums = array(replicanums_ori)[:, start::subs]
714 for state
in range(1, N):
715 tau0[state] = tau0[state - 1] + \
716 state / (float(use_avgAR[state - 1]))
717 for state
in reversed(range(1, N)):
718 tauN[state - 1] = tauN[state] \
719 + (N - (state - 1)) / (float(use_avgAR[state - 1]))
721 return tau0, tauN,
None,
None,
None
729 store0 = zeros((N, N), dtype=bool)
730 last0 = [0
for i
in range(N)]
731 already0 = [
False for i
in range(N)]
732 times0 = [[]
for i
in range(N)]
733 storeN = zeros((N, N), dtype=bool)
734 lastN = [0
for i
in range(N)]
735 alreadyN = [
False for i
in range(N)]
736 timesN = [[]
for i
in range(N)]
739 for time, frame
in enumerate(zip(*replicanums)):
741 if not already0[frame[0]]:
742 last0[frame[0]] = time
743 store0[frame[0], :] =
True
744 already0[frame[0]] =
True
746 if not alreadyN[frame[-1]]:
747 lastN[frame[-1]] = time
748 storeN[frame[-1], :] =
True
749 alreadyN[frame[-1]] =
True
751 already0[frame[1]] =
False
752 alreadyN[frame[-2]] =
False
754 for state, rep
in enumerate(frame):
755 if store0[rep, state]:
757 store0[rep, state] =
False
759 times0[state].append(time - last0[rep])
760 if storeN[rep, state]:
762 storeN[rep, state] =
False
764 timesN[state].append(time - lastN[rep])
767 chose_N = [len(timesN[state]) > len(times0[state])
for state
in
769 for state
in range(N):
770 tauN[state] = sum(timesN[state]) / float(len(timesN[state]))
771 tau0[state] = sum(times0[state]) / float(len(times0[state]))
774 return tau0, tauN, chose_N, times0, timesN
777 def compute_effective_fraction(tau0, tauN, chose_N):
778 """input: tau0, tauN, chose_N
779 output: effective fraction f(T) (P_up(n)) as introduced in
780 Trebst S, Troyer M, Hansmann UHE, J Chem Phys *124* 174903 (2006).
782 Nadler W, Hansmann UHE, Phys Rev E *75* 026109 (2007)
783 and whose calculation is enhanced in
784 Nadler W, Meinke J, Hansmann UHE, Phys Rev E *78* 061905 (2008)
785 the ideal value of f(n) should be 1 - n/N
793 nstar = N - sum([int(a)
for a
in chose_N]) - 1
795 prdb(
"n* = %d" % nstar)
799 for state
in range(2, nstar + 1):
800 h0[state] = h0[state - 1] + \
801 (tau0[state] - tau0[state - 1]) / float(state)
805 for state
in reversed(range(nstar, N - 1)):
806 hN[state] = hN[state + 1] + \
807 (tauN[state] - tauN[state + 1]) / float(N - state)
812 for n
in range(1, nstar + 1):
813 pup[n] = 1 - h0[n] / (h0[nstar] + hN[nstar])
814 for n
in range(nstar + 1, N):
815 pup[n] = hN[n] / (h0[nstar] + hN[nstar])
820 def spline_diffusivity(pup, params):
821 """spline interpolation of diffusivity: D = 1/(df/dT * heta)
823 from numpy
import linspace
824 robjects.globalenv[
"hetay"] = \
825 robjects.FloatVector(linspace(0, 1, num=len(params)).tolist())
826 robjects.globalenv[
"hetax"] = robjects.FloatVector(params)
827 robjects.globalenv[
"pupx"] = robjects.FloatVector(params)
828 robjects.globalenv[
"pupy"] = robjects.FloatVector(pup)
829 _ = r(
'heta <- splinefun(hetax,hetay,method="monoH.FC")')
830 _ = r(
'eff <- splinefun(pupx,pupy,method="monoH.FC")')
831 diff = r(
'diff <- function(x) {-1/(heta(x,deriv=1)*eff(x,deriv=1))}')
832 return lambda x: diff(x)[0]
836 def compute_indicators(replicanums, subs=1, start=0):
837 """input: replicanums : a list of N lists of size M, where N is the number
838 of states and M is the length of the simulation. Each element is an
839 integer, and corresponds to the label of a replica.
840 output: an indicator function of exchanges (size (N-1)x(M-1)), 1 if
841 exchange and 0 if not.
844 if replicanums[n][m] == replicanums[n + 1][m + 1] \
845 and replicanums[n][m + 1] == replicanums[n + 1][m]:
851 for n
in range(len(replicanums) - 1):
854 for m
in range(len(replicanums[n]) - 1)][start::subs])
860 def update_params_nonergodic(pup, params, write_g=False, num=False):
862 from numpy
import linspace
864 g = linear_interpolation(list(zip(pup, params)), 0)
866 d = spline_diffusivity(pup, params)
868 fl.write(
"".join([
"%f %f\n" % (x, g(x))
869 for x
in linspace(0, 1, num=100)]))
871 fl = open(
'diffusivity',
'w')
872 fl.write(
''.join([
"%f %f\n" % (x, d(x))
for x
in
873 linspace(params[0], params[-1], num=100)]))
875 fl = open(
'pup',
'w')
876 fl.write(
"".join([
"%f %f\n" % (i, j)
for (i, j)
in zip(params, pup)]))
880 newparams = [g(i)
for i
in reversed(linspace(0, 1, num=len(params)))]
882 newparams = [g(i)
for i
in reversed(linspace(0, 1, num=num))]
884 newparams[0] = params[0]
885 newparams[-1] = params[-1]
891 indicators, params, isGood, targetAR=0.4, immobilePoint=1,
892 Cv=
None, badMethod=
"dumb", goodMethod=
"dumb", dumb_scale=0.1):
893 """update the parameters according to the isGood tuple and using the
896 newparams = list(params)
898 if immobilePoint != 1:
899 raise NotImplementedError
901 if Cv
is None and (badMethod !=
"dumb" or goodMethod !=
"dumb"):
902 raise RuntimeError(
"""Cv needs to be estimated if using other methods
903 than 'dumb' for updating!""")
905 if goodMethod ==
"dumb":
906 update_good = update_good_dumb
907 elif goodMethod ==
"step":
908 update_good = update_any_cv_step
909 elif goodMethod ==
"sc":
910 update_good = update_any_cv_sc
911 elif goodMethod ==
"scfull":
912 update_good = update_any_cv_scfull
913 elif goodMethod ==
"nr":
914 update_good = update_any_cv_nr
916 raise NotImplementedError(goodMethod)
917 if badMethod ==
"dumb":
918 update_bad = update_bad_dumb
919 elif badMethod ==
"step":
920 update_bad = update_any_cv_step
921 elif badMethod ==
"sc":
922 update_bad = update_any_cv_sc
923 elif badMethod ==
"scfull":
924 update_bad = update_any_cv_scfull
925 elif badMethod ==
"nr":
926 update_bad = update_any_cv_nr
928 raise NotImplementedError(badMethod)
931 for pos
in range(len(params) - 1):
933 newparams[pos + 1] = update_good(
934 newparams[pos:pos + 2], params[pos:pos + 2],
935 indicators[pos], targetAR=targetAR, Cv=Cv)
937 newparams[pos + 1] = update_bad(
938 newparams[pos:pos + 2], params[pos:pos + 2],
939 indicators[pos], targetAR=targetAR, Cv=Cv, scale=dumb_scale)
941 return tuple(newparams)
944 def tune_params_flux(replicanums, params, subs=1, start=0, alpha=0.05,
945 testMethod=
'anova', meanMethod=
'binom', use_avgAR=
False,
946 power=0.8, num=
False):
951 if use_avgAR
is not False:
952 raise NotImplementedError
954 prdb(
"computing mean first passage times")
955 tau0, tauN, chose_N, times0, timesN = mean_first_passage_times(
956 replicanums, subs=subs, start=start, use_avgAR=use_avgAR)
958 prdb(
"average round trip time: %.2f (%d+%d events)" %
959 (tau0[-1] + tauN[0], len(times0[-1]), len(timesN[0])))
960 prdb(
"checking if the parameterset needs to be improved")
965 nstar = N - sum([int(a)
for a
in chose_N]) - 1
969 for n
in range(1, N - 1):
971 reduced.append([i * 2.0 / ((N - n) * (N - n + 1))
974 reduced.append([i * 2.0 / (n * (n + 1))
for i
in times0[n]])
976 anova_result = are_equal(reduced, alpha=alpha, method=testMethod,
978 if (anova_result[
'result']):
979 prdb(
"flux is constant, nothing to do!")
980 min_n = minimum_n(anova_result, alpha)
981 prdb(
'Try to rerun this test with at least %d more samples.' %
983 return (
False, min_n)
987 prdb(
"parameterset not optimal, computing effective fraction")
988 pup = compute_effective_fraction(tau0, tauN, chose_N)
991 prdb(
"returning new parameterset")
992 params = update_params_nonergodic(pup, params, num=num)
994 return (
True, params)
997 def tune_params_ar(indicators, params, targetAR=0.4, alpha=0.05,
998 immobilePoint=1, CvMethod=
"skip", badMethod=
"dumb",
999 goodMethod=
"dumb", varMethod=
"skip", testMethod=
"anova",
1000 meanMethod=
"binom", energies=
None, temps=
None, power=0.8,
1002 """Tune the replica-exchange parameters and return a new set.
1005 indicators -- an (N-1)x(M-1) table, where entry at position (j,i)
1006 is True if replica j and j+1 performed an exchange between
1008 params -- the current set of N parameters used in the simulation.
1011 targetAR -- the target AR which is wanted for the simulation
1013 alpha -- the type 1 error on the one-way ANOVA and subsequent
1014 t-tests (default: 5%)
1015 immobilePoint -- which replica should keep it's parameter fixed
1016 (default: 1st replica, e.g. 1)
1017 CvMethod -- the heat capacity estimation method (default:
1018 "skip", other options: "mbar", "spline", "constant")
1019 badMethod -- how to correct the (j+1)th parameter if the
1020 acceptance ratio between replicas j and j+1 is off the
1021 target value (default: "dumb", options: "step", "sc",
1023 goodMethod -- how to update the value of the (j+1)th parameter
1024 in the case of a correctly exchanging couple, but if the jth
1025 parameter has been modified (default: "dumb",options: "step",
1026 "sc" self-consistent, "scfull" self-consistent using the exact
1027 equation, "nr" newton-raphson solver for the exact equation)
1028 dumb_scale -- (0.0-1.0) in the "dumb" method, scale wrong temperature
1029 intervals by this amount. (default: 0.1)
1030 testMethod -- how to test for the difference of the means,
1031 either "anova" for a one-way anova, or "kruskal" for a
1032 Kruskal-Wallis one-way non-parametric anova.
1033 meanMethod -- "ttest" for a two-sided one-sample t-test,
1034 "binom" for an exact binomial test of the probability.
1035 varMethod -- how to test for the equality of variances.
1036 "fligner" for the Fligner-Killeen non-parametric test,
1037 "bartlett" for Bartlett's test, "skip" to pass.
1038 energies -- if CvMethod is set to "mbar", the energies of each
1039 state as a function of time are used to estimate the heat capacity.
1040 temps -- the temperatures of the simulations, if estimating
1044 returns a tuple: (bool, params). bool is True if params have
1045 changed, and params is the new set.
1050 prdb(
"performing ANOVA")
1051 anova_result = are_equal(indicators, targetAR, alpha, method=testMethod,
1052 varMethod=varMethod, power=power)
1053 if (anova_result[
'result']
1054 and are_equal_to_targetAR(indicators, targetAR, alpha,
1055 method=meanMethod)):
1056 prdb(
"all means are equal to target AR, nothing to do!")
1057 min_n = minimum_n(anova_result, alpha)
1059 'Try to rerun this test with at least %d more samples.' %
1061 return (
False, min_n)
1062 prdb(
"some means are different, performing t-tests")
1065 isGood = find_good_ARs(indicators, targetAR, alpha, method=meanMethod)
1066 if not (
False in isGood):
1067 prdb(
"""Bad luck: ANOVA says means are not identical, but t-tests
1068 can't find the bad rates...""")
1069 return (
False, params)
1073 prdb(
"performing stationarity test")
1074 if not are_stationnary(indicators, alpha):
1075 prdb(
"Warning: Some simulations are not stationary!")
1079 prdb(
"launching Cv estimation or skipping it")
1080 if CvMethod ==
"skip":
1082 elif CvMethod ==
"interpolate" or CvMethod ==
"constant":
1083 Cv =
CvEstimator(params, indicators=indicators, method=CvMethod)
1084 elif CvMethod ==
"mbar":
1091 raise NotImplementedError(CvMethod)
1094 prdb(
'updating params')
1096 params = update_params(indicators, params, isGood, targetAR=targetAR,
1097 immobilePoint=immobilePoint, Cv=Cv,
1098 badMethod=badMethod, goodMethod=goodMethod,
1099 dumb_scale=dumb_scale)
1102 return (
True, params)
1105 if __name__ ==
'__main__':
1108 for i
in range(1, 8):
1110 tuple(numpy.fromfile(
'data/replica-indices/%d.rep' % i,
1111 dtype=int, sep=
'\n')))
1113 prdb(
"replicanums: %dx%d" % (len(replicanums), len(replicanums[0])))
1114 params = tuple(numpy.fromfile(
'data/temperatures', sep=
' '))
1117 indicators = compute_indicators(replicanums, subs=1, start=0)
1118 prdb(
"indicators: %dx%d" % (len(indicators), len(indicators[0])))
1119 prdb(
"Exchange rate:")
1120 prdb(numpy.array([sum(ind) / float(len(ind))
for ind
in indicators]))
1121 numpy.array([sum(ind) / float(len(ind))
1122 for ind
in indicators]).tofile(
'xchgs', sep=
'\n')
1123 changed, newparams = tune_params_ar(
1124 indicators, params, targetAR=0.25,
1125 badMethod=
"dumb", goodMethod=
"dumb", CvMethod=
"skip",
1126 testMethod=
"anova", alpha=0.05)
1128 print(
"Parameter set seems optimal.")
1130 if True not in [abs(newparams[i + 1] - newparams[i]) < 1e-3
1131 for i
in range(len(newparams) - 1)]:
1132 numpy.array(newparams).tofile(
'data/temperatures', sep=
' ')
1134 print(
"PROBLEM IN NEW PARAMETERSET -> not saved")
1135 print(
"params :", params)
1136 print(
"new params:", newparams)
def estimate_cv_constant
try to guess which constant cv fits best
def estimate_cv_interpolate
interpolate using previous values, by reversing the approximate overlap function
When created, estimates the heat capacity from the energies or from the indicator functions using the...
def mean_interp
estimate the mean of Cv between two points.
def estimate_cv_mbar
use MBAR to get the heat capacity
def get_mbar
returns the point estimate of the first derivative of the energy with respect to the replica exchange...
def get_interp
returns the point estimate of the first derivative of the energy with respect to the replica exchange...