1 """
2 Abstract Monte Carlo samplers.
3 """
4
5 import numpy.random
6
7 import csb.numeric
8 import csb.core
9
10 from abc import ABCMeta, abstractmethod, abstractproperty
11 from csb.statistics.samplers import AbstractSampler, AbstractState, State, EnsembleState
14 """
15 Abstract Monte Carlo sampler class. Subclasses implement various
16 Monte carlo equilibrium sampling schemes.
17
18 @param state: Initial state
19 @type state: L{AbstractState}
20 """
21
22 __metaclass__ = ABCMeta
23
28
33
34 @abstractproperty
36 """
37 Energy of the current state.
38 """
39 pass
40
41 @property
43 """
44 Current state.
45 """
46 return self._state
47 @state.setter
51
52 @abstractmethod
54 """
55 Draw a sample.
56 @rtype: L{AbstractState}
57 """
58 pass
59
61 """
62 Abstract class providing the interface for the result
63 of a deterministic or stochastic propagation of a state.
64 """
65
66 __metaclass__ = ABCMeta
67
68 @abstractproperty
70 """
71 Initial state
72 """
73 pass
74
75 @abstractproperty
77 """
78 Final state
79 """
80 pass
81
82 @abstractproperty
84 """
85 Heat produced during propagation
86 @rtype: float
87 """
88 pass
89
91 """
92 Describes the result of a deterministic or stochastic
93 propagation of a state.
94
95 @param initial: Initial state from which the
96 propagation started
97 @type initial: L{State}
98
99 @param final: Final state in which the propagation
100 resulted
101 @type final: L{State}
102
103 @param heat: Heat produced during propagation
104 @type heat: float
105 """
106
107
108 - def __init__(self, initial, final, heat=0.0, ):
122
123 @property
126
127 @property
130
131 @property
134 @heat.setter
135 - def heat(self, value):
136 self._heat = float(value)
137
138 -class Trajectory(csb.core.CollectionContainer, AbstractPropagationResult):
139 """
140 Ordered collection of states, representing a phase-space trajectory.
141
142 @param items: list of states defining a phase-space trajectory
143 @type items: list of L{AbstractState}
144 @param heat: heat produced during the trajectory
145 @type heat: float
146 @param work: work produced during the trajectory
147 @type work: float
148 """
149
150 - def __init__(self, items, heat=0.0, work=0.0):
156
157 @property
160
161 @property
164
165 @property
168 @heat.setter
169 - def heat(self, value):
170 self._heat = float(value)
171
172 @property
175 @work.setter
176 - def work(self, value):
177 self._work = float(value)
178
180 """
181 Allows to build a Trajectory object step by step.
182
183 @param heat: heat produced over the trajectory
184 @type heat: float
185 @param work: work produced during the trajectory
186 @type work: float
187 """
188
189 - def __init__(self, heat=0.0, work=0.0):
190 self._heat = heat
191 self._work = work
192 self._states = []
193
194 @staticmethod
196 """
197 Trajectory builder factory.
198
199 @param full: if True, a TrajectoryBuilder instance designed
200 to build a full trajectory with initial state,
201 intermediate states and a final state. If False,
202 a ShortTrajectoryBuilder instance designed to
203 hold only the initial and the final state is
204 returned
205 @type full: boolean
206 """
207
208 if full:
209 return TrajectoryBuilder()
210 else:
211 return ShortTrajectoryBuilder()
212
213 @property
215 """
216 The L{Trajectory} instance build by a specific instance of
217 this class
218 """
219 return Trajectory(self._states, heat=self._heat, work=self._work)
220
222 """
223 Inserts a state at the beginning of the trajectory
224
225 @param state: state to be added
226 @type state: L{State}
227 """
228 self._states.insert(0, state.clone())
229
238
240 """
241 Adds a state to the end of the trajectory
242
243 @param state: state to be added
244 @type state: L{State}
245 """
246 self._states.append(state.clone())
247
249
252
253 @property
255 """
256 The L{PropagationResult} instance built by a specific instance of
257 this class
258 """
259
260 if len(self._states) != 2:
261 raise ValueError("Can't create a product, two states required")
262
263 initial, final = self._states
264 return PropagationResult(initial, final, heat=self._heat)
265
267 """
268 With the exception of the current state of the Markov chain, this
269 holds all the information needed to calculate the acceptance
270 probability in both the L{RWMCSampler} and L{HMCSampler} classes,
271 that is, only the proposal state.
272 For more advanced algorithms, one may derive classes capable of
273 holding the neccessary additional information from this class.
274
275 @param proposal: Proposal state
276 @type proposal: L{State}
277 """
278
279 __metaclass__ = ABCMeta
280
284
285 @property
287 return self._proposal
288
290 """
291 Abstract class for Monte Carlo sampling algorithms simulating
292 only one ensemble.
293
294 @param pdf: probability density function to sample from
295 @type pdf: subclass of L{csb.statistics.pdf.AbstractDensity}
296
297 @param state: Initial state
298 @type state: L{State}
299
300 @param temperature: Pseudo-temperature of the Boltzmann ensemble
301 M{p(x) = 1/N * exp(-1/T * E(x))} with the
302 pseudo-energy defined as M{E(x) = -log(p(x))}
303 where M{p(x)} is the PDF under consideration
304 @type temperature: float
305 """
306
307 __metaclass__ = ABCMeta
308
309 - def __init__(self, pdf, state, temperature=1.):
318
322
324 """
325 Draw a sample.
326 @rtype: L{State}
327 """
328
329 proposal_communicator = self._propose()
330 pacc = self._calc_pacc(proposal_communicator)
331 self._accept(proposal_communicator.proposal, pacc)
332
333 return self.state
334
335 @abstractmethod
337 """
338 Calculate a new proposal state and gather additional information
339 needed to calculate the acceptance probability.
340
341 @rtype: L{AbstractProposalCommunicator}
342 """
343 pass
344
345 @abstractmethod
347 """
348 Calculate probability with which to accept the proposal.
349
350 @param proposal_communicator: Contains information about the proposal
351 and additional information needed to
352 calculate the acceptance probability
353 @type proposal_communicator: L{AbstractProposalCommunicator}
354 """
355 pass
356
357 - def _accept(self, proposal, pacc):
358 """
359 Accept / reject proposal with given acceptance probability pacc.
360
361 @param proposal: proposal state
362 @type proposal: L{State}
363
364 @param pacc: acceptance probability
365 @type pacc: float
366 """
367
368 self._nmoves += 1
369
370 if numpy.random.random() < pacc:
371 self.state = proposal
372 self._accepted += 1
373 self._last_move_accepted = True
374 return True
375 else:
376 self._last_move_accepted = False
377 return False
378
379 @property
381 """
382 Log-likelihood of the current state.
383 @rtype: float
384 """
385 return self._pdf.log_prob(self.state.position)
386
387 @property
389 """
390 Acceptance rate.
391 """
392 return float(self._accepted) / float(self._nmoves)
393
394 @property
396 """
397 Information whether the last MC move was accepted or not.
398 """
399 return self._last_move_accepted
400
401 @property
403 return self._temperature
404
406 """
407 Collection of single-chain samplers.
408
409 @param items: samplers
410 @type items: list of L{AbstractSingleChainMC}
411 """
412
416
418 """
419 Abstract class for Monte Carlo sampling algorithms simulating several ensembles.
420
421 @param samplers: samplers which sample from their respective equilibrium distributions
422 @type samplers: list of L{AbstractSingleChainMC}
423 """
424
425 __metaclass__ = ABCMeta
426
433
445
446 @property
448 """
449 Total ensemble energy.
450 """
451 return sum([x.energy for x in self._samplers])
452
454 """
455 Subclass instances hold all parameters necessary for performing a swap
456 between two given samplers.
457 """
458
459 __metaclass__ = ABCMeta
460
461 - def __init__(self, sampler1, sampler2):
462 """
463 @param sampler1: First sampler
464 @type sampler1: L{AbstractSingleChainMC}
465
466 @param sampler2: Second sampler
467 @type sampler2: L{AbstractSingleChainMC}
468 """
469
470 self._sampler1 = sampler1
471 self._sampler2 = sampler2
472
473 @property
475 return self._sampler1
476
477 @property
479 return self._sampler2
480
482 """
483 Holds parameters for a standard Replica Exchange swap.
484 """
485 pass
486
488 """
489 Holds parameters for a MDRENS swap.
490
491 @param sampler1: First sampler
492 @type sampler1: L{AbstractSingleChainMC}
493
494 @param sampler2: Second sampler
495 @type sampler2: L{AbstractSingleChainMC}
496
497 @param timestep: Integration timestep
498 @type timestep: float
499
500 @param traj_length: Trajectory length in number of timesteps
501 @type traj_length: int
502
503 @param gradient: Gradient which determines the dynamics during a trajectory
504 @type gradient: L{AbstractGradient}
505 """
506
507 - def __init__(self, sampler1, sampler2, timestep, traj_length, gradient):
514
515 @property
517 """
518 Integration timestep.
519 """
520 return self._timestep
521 @timestep.setter
523 self._timestep = float(value)
524
525 @property
527 """
528 Trajectory length in number of integration steps.
529 """
530 return self._traj_length
531 @traj_length.setter
533 self._traj_length = int(value)
534
535 @property
537 """
538 Gradient which governs the equations of motion.
539 """
540 return self._gradient
541
543 """
544 @param sampler1: First sampler
545 @type sampler1: subclass instance of L{AbstractSingleChainMC}
546
547 @param sampler2: Second sampler
548 @type sampler2: subclass instance of L{AbstractSingleChainMC}
549
550 @param timestep: Integration timestep
551 @type timestep: float
552
553 @param traj_length: Trajectory length in number of timesteps
554 @type traj_length: int
555
556 @param gradient: Gradient which determines the dynamics during a trajectory
557 @type gradient: subclass instance of L{AbstractGradient}
558
559 @param temperature: Temperature interpolation function.
560 @type temperature: Real-valued function mapping from [0,1] to R.
561 T(0) = temperature of the ensemble sampler1 samples from, T(1) = temperature
562 of the ensemble sampler2 samples from
563
564 @param collision_probability: Probability for a collision with the heatbath during one timestep
565 @type collision_probability: float
566
567 @param collision_interval: Interval during which collision may occur with probability
568 collision_probability
569 @type collision_interval: int
570 """
571
584
585 @property
587 """
588 Probability for a collision with the heatbath during one timestep.
589 """
590 return self._collision_probability
591 @collision_probability.setter
593 self._collision_probability = float(value)
594
595 @property
597 """
598 Interval during which collision may occur with probability
599 C{collision_probability}.
600 """
601 return self._collision_interval
602 @collision_interval.setter
604 self._collision_interval = int(value)
605
606 @property
608 return self._temperature
609
611 """
612 Holds all the information which needs to be communicated between
613 distinct swap substeps.
614
615 @param param_info: ParameterInfo instance holding swap parameters
616 @type param_info: L{AbstractSwapParameterInfo}
617
618 @param proposal1: Proposal state for first sampler
619 @type proposal1: L{State}
620
621 @param proposal2: Proposal state for second sampler
622 @type proposal2: L{State}
623 """
624
625 __metaclass__ = ABCMeta
626
627 - def __init__(self, param_info, proposal1, proposal2):
642
643 @property
645 return self._sampler1
646
647 @property
649 return self._sampler2
650
651 @property
654
655 @property
658
659 @property
661 return self._proposal1
662
663 @property
665 return self._proposal2
666
667 @property
669 return self._acceptance_probability
670 @acceptance_probability.setter
672 self._acceptance_probability = value
673
674 @property
676 return self._accepted
677 @accepted.setter
679 self._accepted = value
680
681 @property
683 return self._param_info
684
686 """
687 Holds all the information which needs to be communicated between distinct
688 RE swap substeps.
689
690 See L{AbstractSwapCommunicator} for constructor signature.
691 """
692 pass
693
695 """
696 Holds all the information which needs to be communicated between distinct
697 RENS swap substeps.
698
699 @param param_info: ParameterInfo instance holding swap parameters
700 @type param_info: L{AbstractSwapParameterInfo}
701
702 @param proposal1: Proposal state for first sampler
703 @type proposal1: L{State}
704
705 @param proposal2: Proposal state for second sampler
706 @type proposal2: L{State}
707
708 @param heat12: Heat generated during the forward trajectory
709 @type heat12: float
710
711 @param heat21: Heat generated during the reverse trajectory
712 @type heat21: float
713 """
714
715 - def __init__(self, param_info, proposal1, proposal2, heat12, heat21):
720
721 @property
724
725 @property
728
730 """
731 Tracks swap statistics of a single sampler pair.
732
733 @param param_info: ParameterInfo instance holding swap parameters
734 @type param_info: L{AbstractSwapParameterInfo}
735 """
736
738 self._total_swaps = 0
739 self._accepted_swaps = 0
740
741 @property
743 return self._total_swaps
744
745 @property
747 return self._accepted_swaps
748
749 @property
758
760 """
761 Updates swap statistics.
762 """
763 self._total_swaps += 1
764 self._accepted_swaps += int(accepted)
765
767 """
768 Tracks swap statistics for an AbstractExchangeMC subclass instance.
769
770 @param param_infos: list of ParameterInfo instances providing information
771 needed for performing swaps
772 @type param_infos: list of L{AbstractSwapParameterInfo}
773 """
774
776 self._stats = [SingleSwapStatistics(x) for x in param_infos]
777
778 @property
780 return tuple(self._stats)
781
782 @property
784 """
785 Returns acceptance rates for all swaps.
786 """
787 return [x.acceptance_rate for x in self._stats]
788
790 """
791 Abstract class for Monte Carlo sampling algorithms employing some replica exchange method.
792
793 @param samplers: samplers which sample from their respective equilibrium distributions
794 @type samplers: list of L{AbstractSingleChainMC}
795
796 @param param_infos: list of ParameterInfo instances providing information needed
797 for performing swaps
798 @type param_infos: list of L{AbstractSwapParameterInfo}
799 """
800
801 __metaclass__ = ABCMeta
802
803 - def __init__(self, samplers, param_infos):
812
816
817 - def swap(self, index):
818 """
819 Perform swap between sampler pair described by param_infos[index]
820 and return outcome (true = accepted, false = rejected).
821
822 @param index: index of swap pair in param_infos
823 @type index: int
824
825 @rtype: boolean
826 """
827 param_info = self._param_infos[index]
828 swapcom = self._propose_swap(param_info)
829 swapcom = self._calc_pacc_swap(swapcom)
830 result = self._accept_swap(swapcom)
831
832 self.state = EnsembleState([x.state for x in self._samplers])
833 self.statistics.stats[index].update(result)
834
835 return result
836
837 @abstractmethod
839 """
840 Calculate proposal states for a swap between two samplers.
841
842 @param param_info: ParameterInfo instance holding swap parameters
843 @type param_info: L{AbstractSwapParameterInfo}
844
845 @rtype: L{AbstractSwapCommunicator}
846 """
847 pass
848
849 @abstractmethod
851 """
852 Calculate probability to accept a swap given initial and proposal states.
853
854 @param swapcom: SwapCommunicator instance holding information to be communicated
855 between distinct swap substeps
856 @type swapcom: L{AbstractSwapCommunicator}
857
858 @rtype: L{AbstractSwapCommunicator}
859 """
860 pass
861
863 """
864 Accept / reject an exchange between two samplers given proposal states and
865 the acceptance probability and returns the outcome (true = accepted, false = rejected).
866
867 @param swapcom: SwapCommunicator instance holding information to be communicated
868 between distinct swap substeps
869 @type swapcom: L{AbstractSwapCommunicator}
870
871 @rtype: boolean
872 """
873
874 if numpy.random.random() < swapcom.acceptance_probability:
875 swapcom.sampler1.state = swapcom.proposal1
876 swapcom.sampler2.state = swapcom.proposal2
877 return True
878 else:
879 return False
880
881 @property
889
890 @property
892 """
893 List of SwapParameterInfo instances holding all necessary parameters.
894
895 @rtype: list of L{AbstractSwapParameterInfo}
896 """
897 return self._param_infos
898
899 @property
901 return self._statistics
902
904 """
905 Update statistics of a given swap process.
906
907 @param index: position of swap statistics to be updated
908 @type index: int
909
910 @param accepted: outcome of the swap
911 @type accepted: boolean
912 """
913
914 self._stats[index][0] += 1
915 self._stats[index][1] += int(accepted)
916
918 """
919 Holds information necessary for calculating a RENS trajectory.
920
921 @param param_info: ParameterInfo instance holding swap parameters
922 @type param_info: L{AbstractSwapParameterInfo}
923
924 @param init_state: state from which the trajectory is supposed to start
925 @type init_state: L{State}
926
927 @param protocol: Protocol to be used to generate nonequilibrium trajectories
928 @type protocol: Real-valued function that maps [0, switching time] to [0, 1]
929 """
930
931 - def __init__(self, param_info, init_state, protocol):
936
937 @property
939 return self._param_info
940
941 @property
943 return self._protocol
944
945 @property
947 return self._init_state
948
950 """
951 Abstract Replica Exchange with Nonequilibrium Switches
952 (RENS, Ballard & Jarzynski 2009) class.
953 Subclasses implement various ways of generating trajectories
954 (deterministic or stochastic).
955 """
956
957 __metaclass__ = ABCMeta
958
960
961 T1 = param_info.sampler1.temperature
962 T2 = param_info.sampler2.temperature
963
964 init_state1 = State(param_info.sampler1.state.position,
965 numpy.random.normal(size=param_info.sampler1.state.position.shape,
966 scale=numpy.sqrt(T1)))
967 init_state2 = State(param_info.sampler2.state.position,
968 numpy.random.normal(size=param_info.sampler2.state.position.shape,
969 scale=numpy.sqrt(T2)))
970
971 param_info.sampler1.state = init_state1
972 param_info.sampler2.state = init_state2
973
974 trajinfo12 = RENSTrajInfo(param_info, init_state1, protocol=lambda t, tau: t / tau)
975 trajinfo21 = RENSTrajInfo(param_info, init_state2, protocol=lambda t, tau: (tau - t) / tau)
976
977 traj12 = self._run_traj_generator(trajinfo12)
978 traj21 = self._run_traj_generator(trajinfo21)
979
980 return RENSSwapCommunicator(param_info, traj21.final, traj12.final, traj12.heat, traj21.heat)
981
1011
1012 @abstractmethod
1014 """
1015 Run the trajectory generator which generates a trajectory
1016 of a given length between the states of two samplers.
1017
1018 @param traj_info: TrajectoryInfo instance holding information
1019 needed to generate a nonequilibrium trajectory
1020 @type traj_info: L{RENSTrajInfo}
1021
1022 @rtype: L{Trajectory}
1023 """
1024 pass
1025
1027 """
1028 Provides the interface for classes defining schemes according to which swaps in
1029 Replica Exchange-like simulations are performed.
1030
1031 @param algorithm: Exchange algorithm that performs the swaps
1032 @type algorithm: L{AbstractExchangeMC}
1033 """
1034
1035 __metaclass__ = ABCMeta
1036
1038
1039 self._algorithm = algorithm
1040
1041 @abstractmethod
1043 """
1044 Advises the Replica Exchange-like algorithm to perform swaps according to
1045 the some schedule defined here.
1046 """
1047
1048 pass
1049
1051 """
1052 Provides a swapping scheme in which tries exchanges between neighbours only
1053 following the scheme 1 <-> 2, 3 <-> 4, ... and after a sampling period 2 <-> 3, 4 <-> 5, ...
1054
1055 @param algorithm: Exchange algorithm that performs the swaps
1056 @type algorithm: L{AbstractExchangeMC}
1057 """
1058
1060
1061 super(AlternatingAdjacentSwapScheme, self).__init__(algorithm)
1062
1063 self._current_swap_list = None
1064 self._swap_list1 = []
1065 self._swap_list2 = []
1066 self._create_swap_lists()
1067
1069
1070 i = 0
1071 while i < len(self._algorithm.param_infos):
1072 self._swap_list1.append(i)
1073 i += 2
1074
1075 i = 1
1076 while i < len(self._algorithm.param_infos):
1077 self._swap_list2.append(i)
1078 i += 2
1079
1080 self._current_swap_list = self._swap_list1
1081
1083
1084 for x in self._current_swap_list:
1085 self._algorithm.swap(x)
1086
1087 if self._current_swap_list == self._swap_list1:
1088 self._current_swap_list = self._swap_list2
1089 else:
1090 self._current_swap_list = self._swap_list1
1091