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
301 __metaclass__ = ABCMeta
302
311
315
317 """
318 Draw a sample.
319 @rtype: L{State}
320 """
321
322 proposal_communicator = self._propose()
323 pacc = self._calc_pacc(proposal_communicator)
324 self._accept(proposal_communicator.proposal, pacc)
325
326 return self.state
327
328 @abstractmethod
330 """
331 Calculate a new proposal state and gather additional information
332 needed to calculate the acceptance probability.
333
334 @rtype: L{AbstractProposalCommunicator}
335 """
336 pass
337
338 @abstractmethod
340 """
341 Calculate probability with which to accept the proposal.
342
343 @param proposal_communicator: Contains information about the proposal
344 and additional information needed to
345 calculate the acceptance probability
346 @type proposal_communicator: L{AbstractProposalCommunicator}
347 """
348 pass
349
350 - def _accept(self, proposal, pacc):
351 """
352 Accept / reject proposal with given acceptance probability pacc.
353
354 @param proposal: proposal state
355 @type proposal: L{State}
356
357 @param pacc: acceptance probability
358 @type pacc: float
359 """
360
361 self._nmoves += 1
362
363 if numpy.random.random() < pacc:
364 self.state = proposal
365 self._accepted += 1
366 self._last_move_accepted = True
367 return True
368 else:
369 self._last_move_accepted = False
370 return False
371
372 @property
374 """
375 Log-likelihood of the current state.
376 @rtype: float
377 """
378 return self._pdf.log_prob(self.state.position)
379
380 @property
382 """
383 Acceptance rate.
384 """
385 return float(self._accepted) / float(self._nmoves)
386
387 @property
389 """
390 Information whether the last MC move was accepted or not.
391 """
392 return self._last_move_accepted
393
395 """
396 Collection of single-chain samplers.
397
398 @param items: samplers
399 @type items: list of L{AbstractSingleChainMC}
400 """
401
405
407 """
408 Abstract class for Monte Carlo sampling algorithms simulating several ensembles.
409
410 @param samplers: samplers which sample from their respective equilibrium distributions
411 @type samplers: list of L{AbstractSingleChainMC}
412 """
413
414 __metaclass__ = ABCMeta
415
422
434
435 @property
437 """
438 Total ensemble energy.
439 """
440 return sum([x.energy for x in self._samplers])
441
443 """
444 Subclass instances hold all parameters necessary for performing a swap
445 between two given samplers.
446 """
447
448 __metaclass__ = ABCMeta
449
450 - def __init__(self, sampler1, sampler2):
451 """
452 @param sampler1: First sampler
453 @type sampler1: L{AbstractSingleChainMC}
454
455 @param sampler2: Second sampler
456 @type sampler2: L{AbstractSingleChainMC}
457 """
458
459 self._sampler1 = sampler1
460 self._sampler2 = sampler2
461
462 @property
464 return self._sampler1
465
466 @property
468 return self._sampler2
469
471 """
472 Holds parameters for a standard Replica Exchange swap.
473 """
474 pass
475
477 """
478 Holds parameters for a MDRENS swap.
479
480 @param sampler1: First sampler
481 @type sampler1: L{AbstractSingleChainMC}
482
483 @param sampler2: Second sampler
484 @type sampler2: L{AbstractSingleChainMC}
485
486 @param timestep: Integration timestep
487 @type timestep: float
488
489 @param traj_length: Trajectory length in number of timesteps
490 @type traj_length: int
491
492 @param gradient: Gradient which determines the dynamics during a trajectory
493 @type gradient: L{AbstractGradient}
494 """
495
496 - def __init__(self, sampler1, sampler2, timestep, traj_length, gradient):
503
504 @property
506 """
507 Integration timestep.
508 """
509 return self._timestep
510 @timestep.setter
512 self._timestep = float(value)
513
514 @property
516 """
517 Trajectory length in number of integration steps.
518 """
519 return self._traj_length
520 @traj_length.setter
522 self._traj_length = int(value)
523
524 @property
526 """
527 Gradient which governs the equations of motion.
528 """
529 return self._gradient
530
532 """
533 @param sampler1: First sampler
534 @type sampler1: subclass instance of L{AbstractSingleChainMC}
535
536 @param sampler2: Second sampler
537 @type sampler2: subclass instance of L{AbstractSingleChainMC}
538
539 @param timestep: Integration timestep
540 @type timestep: float
541
542 @param traj_length: Trajectory length in number of timesteps
543 @type traj_length: int
544
545 @param gradient: Gradient which determines the dynamics during a trajectory
546 @type gradient: subclass instance of L{AbstractGradient}
547
548 @param temperature: Temperature interpolation function.
549 @type temperature: Real-valued function mapping from [0,1] to R.
550 T(0) = temperature of the ensemble sampler1 samples from, T(1) = temperature
551 of the ensemble sampler2 samples from
552
553 @param collision_probability: Probability for a collision with the heatbath during one timestep
554 @type collision_probability: float
555
556 @param collision_interval: Interval during which collision may occur with probability
557 collision_probability
558 @type collision_interval: int
559 """
560
573
574 @property
576 """
577 Probability for a collision with the heatbath during one timestep.
578 """
579 return self._collision_probability
580 @collision_probability.setter
582 self._collision_probability = float(value)
583
584 @property
586 """
587 Interval during which collision may occur with probability
588 C{collision_probability}.
589 """
590 return self._collision_interval
591 @collision_interval.setter
593 self._collision_interval = int(value)
594
595 @property
597 return self._temperature
598
600 """
601 Holds all the information which needs to be communicated between
602 distinct swap substeps.
603
604 @param param_info: ParameterInfo instance holding swap parameters
605 @type param_info: L{AbstractSwapParameterInfo}
606
607 @param proposal1: Proposal state for first sampler
608 @type proposal1: L{State}
609
610 @param proposal2: Proposal state for second sampler
611 @type proposal2: L{State}
612 """
613
614 __metaclass__ = ABCMeta
615
616 - def __init__(self, param_info, proposal1, proposal2):
629
630 @property
632 return self._sampler1
633
634 @property
636 return self._sampler2
637
638 @property
641
642 @property
645
646 @property
648 return self._proposal1
649
650 @property
652 return self._proposal2
653
654 @property
656 return self._acceptance_probability
657 @acceptance_probability.setter
659 self._acceptance_probability = value
660
661 @property
663 return self._accepted
664 @accepted.setter
666 self._accepted = value
667
669 """
670 Holds all the information which needs to be communicated between distinct
671 RE swap substeps.
672
673 See L{AbstractSwapCommunicator} for constructor signature.
674 """
675 pass
676
678 """
679 Holds all the information which needs to be communicated between distinct
680 RENS swap substeps.
681
682 @param param_info: ParameterInfo instance holding swap parameters
683 @type param_info: L{AbstractSwapParameterInfo}
684
685 @param proposal1: Proposal state for first sampler
686 @type proposal1: L{State}
687
688 @param proposal2: Proposal state for second sampler
689 @type proposal2: L{State}
690
691 @param heat12: Heat generated during the forward trajectory
692 @type heat12: float
693
694 @param heat21: Heat generated during the reverse trajectory
695 @type heat21: float
696 """
697
698 - def __init__(self, param_info, proposal1, proposal2, heat12, heat21):
703
704 @property
707
708 @property
711
713 """
714 Tracks swap statistics of a single sampler pair.
715
716 @param param_info: ParameterInfo instance holding swap parameters
717 @type param_info: L{AbstractSwapParameterInfo}
718 """
719
721 self._total_swaps = 0
722 self._accepted_swaps = 0
723
724 @property
726 return self._total_swaps
727
728 @property
730 return self._accepted_swaps
731
732 @property
741
743 """
744 Updates swap statistics.
745 """
746 self._total_swaps += 1
747 self._accepted_swaps += int(accepted)
748
750 """
751 Tracks swap statistics for an AbstractExchangeMC subclass instance.
752
753 @param param_infos: list of ParameterInfo instances providing information
754 needed for performing swaps
755 @type param_infos: list of L{AbstractSwapParameterInfo}
756 """
757
759 self._stats = [SingleSwapStatistics(x) for x in param_infos]
760
761 @property
763 return tuple(self._stats)
764
765 @property
767 """
768 Returns acceptance rates for all swaps.
769 """
770 return [x.acceptance_rate for x in self._stats]
771
773 """
774 Abstract class for Monte Carlo sampling algorithms employing some replica exchange method.
775
776 @param samplers: samplers which sample from their respective equilibrium distributions
777 @type samplers: list of L{AbstractSingleChainMC}
778
779 @param param_infos: list of ParameterInfo instances providing information needed
780 for performing swaps
781 @type param_infos: list of L{AbstractSwapParameterInfo}
782 """
783
784 __metaclass__ = ABCMeta
785
786 - def __init__(self, samplers, param_infos):
795
799
800 - def swap(self, index):
801 """
802 Perform swap between sampler pair described by param_infos[index]
803 and return outcome (true = accepted, false = rejected).
804
805 @param index: index of swap pair in param_infos
806 @type index: int
807
808 @rtype: boolean
809 """
810 param_info = self._param_infos[index]
811 swapcom = self._propose_swap(param_info)
812 swapcom = self._calc_pacc_swap(swapcom)
813 result = self._accept_swap(swapcom)
814
815 self.state = EnsembleState([x.state for x in self._samplers])
816 self.statistics.stats[index].update(result)
817
818 return result
819
820 @abstractmethod
822 """
823 Calculate proposal states for a swap between two samplers.
824
825 @param param_info: ParameterInfo instance holding swap parameters
826 @type param_info: L{AbstractSwapParameterInfo}
827
828 @rtype: L{AbstractSwapCommunicator}
829 """
830 pass
831
832 @abstractmethod
834 """
835 Calculate probability to accept a swap given initial and proposal states.
836
837 @param swapcom: SwapCommunicator instance holding information to be communicated
838 between distinct swap substeps
839 @type swapcom: L{AbstractSwapCommunicator}
840
841 @rtype: L{AbstractSwapCommunicator}
842 """
843 pass
844
846 """
847 Accept / reject an exchange between two samplers given proposal states and
848 the acceptance probability and returns the outcome (true = accepted, false = rejected).
849
850 @param swapcom: SwapCommunicator instance holding information to be communicated
851 between distinct swap substeps
852 @type swapcom: L{AbstractSwapCommunicator}
853
854 @rtype: boolean
855 """
856
857 if numpy.random.random() < swapcom.acceptance_probability:
858 swapcom.sampler1.state = swapcom.proposal1
859 swapcom.sampler2.state = swapcom.proposal2
860 return True
861 else:
862 return False
863
864 @property
872
873 @property
875 """
876 List of SwapParameterInfo instances holding all necessary parameters.
877
878 @rtype: list of L{AbstractSwapParameterInfo}
879 """
880 return self._param_infos
881
882 @property
884 return self._statistics
885
887 """
888 Update statistics of a given swap process.
889
890 @param index: position of swap statistics to be updated
891 @type index: int
892
893 @param accepted: outcome of the swap
894 @type accepted: boolean
895 """
896
897 self._stats[index][0] += 1
898 self._stats[index][1] += int(accepted)
899
901 """
902 Holds information necessary for calculating a RENS trajectory.
903
904 @param param_info: ParameterInfo instance holding swap parameters
905 @type param_info: L{AbstractSwapParameterInfo}
906
907 @param init_state: state from which the trajectory is supposed to start
908 @type init_state: L{State}
909
910 @param protocol: Protocol to be used to generate nonequilibrium trajectories
911 @type protocol: Real-valued function that maps [0, switching time] to [0, 1]
912 """
913
914 - def __init__(self, param_info, init_state, protocol):
919
920 @property
922 return self._param_info
923
924 @property
926 return self._protocol
927
928 @property
930 return self._init_state
931
933 """
934 Abstract Replica Exchange with Nonequilibrium Switches
935 (RENS, Ballard & Jarzynski 2009) class.
936 Subclasses implement various ways of generating trajectories
937 (deterministic or stochastic).
938 """
939
940 __metaclass__ = ABCMeta
941
943
944 init_state1 = State(param_info.sampler1.state.position,
945 numpy.random.normal(size=param_info.sampler1.state.position.shape))
946 init_state2 = State(param_info.sampler2.state.position,
947 numpy.random.normal(size=param_info.sampler2.state.position.shape))
948
949 param_info.sampler1.state = init_state1
950 param_info.sampler2.state = init_state2
951
952 trajinfo12 = RENSTrajInfo(param_info, init_state1, protocol=lambda t, tau: t / tau)
953 trajinfo21 = RENSTrajInfo(param_info, init_state2, protocol=lambda t, tau: (tau - t) / tau)
954
955 traj12 = self._run_traj_generator(trajinfo12)
956 traj21 = self._run_traj_generator(trajinfo21)
957
958 return RENSSwapCommunicator(param_info, traj21.final, traj12.final, traj21.heat, traj12.heat)
959
982
983 @abstractmethod
985 """
986 Run the trajectory generator which generates a trajectory
987 of a given length between the states of two samplers.
988
989 @param traj_info: TrajectoryInfo instance holding information
990 needed to generate a nonequilibrium trajectory
991 @type traj_info: L{RENSTrajInfo}
992
993 @rtype: L{Trajectory}
994 """
995 pass
996
998 """
999 Provides the interface for classes defining schemes according to which swaps in
1000 Replica Exchange-like simulations are performed.
1001
1002 @param algorithm: Exchange algorithm that performs the swaps
1003 @type algorithm: L{AbstractExchangeMC}
1004 """
1005
1006 __metaclass__ = ABCMeta
1007
1009
1010 self._algorithm = algorithm
1011
1012 @abstractmethod
1014 """
1015 Advises the Replica Exchange-like algorithm to perform swaps according to
1016 the some schedule defined here.
1017 """
1018
1019 pass
1020
1022 """
1023 Provides a swapping scheme in which tries exchanges between neighbours only
1024 following the scheme 1 <-> 2, 3 <-> 4, ... and after a sampling period 2 <-> 3, 4 <-> 5, ...
1025
1026 @param algorithm: Exchange algorithm that performs the swaps
1027 @type algorithm: L{AbstractExchangeMC}
1028 """
1029
1031
1032 super(AlternatingAdjacentSwapScheme, self).__init__(algorithm)
1033
1034 self._current_swap_list = None
1035 self._swap_list1 = []
1036 self._swap_list2 = []
1037 self._create_swap_lists()
1038
1040
1041 i = 0
1042 while i < len(self._algorithm.param_infos):
1043 self._swap_list1.append(i)
1044 i += 2
1045
1046 i = 1
1047 while i < len(self._algorithm.param_infos):
1048 self._swap_list2.append(i)
1049 i += 2
1050
1051 self._current_swap_list = self._swap_list1
1052
1054
1055 for x in self._current_swap_list:
1056 self._algorithm.swap(x)
1057
1058 if self._current_swap_list == self._swap_list1:
1059 self._current_swap_list = self._swap_list2
1060 else:
1061 self._current_swap_list = self._swap_list1
1062