westpa 2022.10__cp312-cp312-macosx_10_9_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of westpa might be problematic. Click here for more details.

Files changed (150) hide show
  1. westpa/__init__.py +14 -0
  2. westpa/_version.py +21 -0
  3. westpa/analysis/__init__.py +5 -0
  4. westpa/analysis/core.py +746 -0
  5. westpa/analysis/statistics.py +27 -0
  6. westpa/analysis/trajectories.py +360 -0
  7. westpa/cli/__init__.py +0 -0
  8. westpa/cli/core/__init__.py +0 -0
  9. westpa/cli/core/w_fork.py +152 -0
  10. westpa/cli/core/w_init.py +230 -0
  11. westpa/cli/core/w_run.py +77 -0
  12. westpa/cli/core/w_states.py +212 -0
  13. westpa/cli/core/w_succ.py +99 -0
  14. westpa/cli/core/w_truncate.py +59 -0
  15. westpa/cli/tools/__init__.py +0 -0
  16. westpa/cli/tools/ploterr.py +506 -0
  17. westpa/cli/tools/plothist.py +706 -0
  18. westpa/cli/tools/w_assign.py +596 -0
  19. westpa/cli/tools/w_bins.py +166 -0
  20. westpa/cli/tools/w_crawl.py +119 -0
  21. westpa/cli/tools/w_direct.py +547 -0
  22. westpa/cli/tools/w_dumpsegs.py +94 -0
  23. westpa/cli/tools/w_eddist.py +506 -0
  24. westpa/cli/tools/w_fluxanl.py +378 -0
  25. westpa/cli/tools/w_ipa.py +833 -0
  26. westpa/cli/tools/w_kinavg.py +127 -0
  27. westpa/cli/tools/w_kinetics.py +96 -0
  28. westpa/cli/tools/w_multi_west.py +414 -0
  29. westpa/cli/tools/w_ntop.py +213 -0
  30. westpa/cli/tools/w_pdist.py +515 -0
  31. westpa/cli/tools/w_postanalysis_matrix.py +82 -0
  32. westpa/cli/tools/w_postanalysis_reweight.py +53 -0
  33. westpa/cli/tools/w_red.py +486 -0
  34. westpa/cli/tools/w_reweight.py +780 -0
  35. westpa/cli/tools/w_select.py +226 -0
  36. westpa/cli/tools/w_stateprobs.py +111 -0
  37. westpa/cli/tools/w_trace.py +599 -0
  38. westpa/core/__init__.py +0 -0
  39. westpa/core/_rc.py +673 -0
  40. westpa/core/binning/__init__.py +55 -0
  41. westpa/core/binning/_assign.cpython-312-darwin.so +0 -0
  42. westpa/core/binning/assign.py +449 -0
  43. westpa/core/binning/binless.py +96 -0
  44. westpa/core/binning/binless_driver.py +54 -0
  45. westpa/core/binning/binless_manager.py +190 -0
  46. westpa/core/binning/bins.py +47 -0
  47. westpa/core/binning/mab.py +427 -0
  48. westpa/core/binning/mab_driver.py +54 -0
  49. westpa/core/binning/mab_manager.py +198 -0
  50. westpa/core/data_manager.py +1694 -0
  51. westpa/core/extloader.py +74 -0
  52. westpa/core/h5io.py +995 -0
  53. westpa/core/kinetics/__init__.py +24 -0
  54. westpa/core/kinetics/_kinetics.cpython-312-darwin.so +0 -0
  55. westpa/core/kinetics/events.py +147 -0
  56. westpa/core/kinetics/matrates.py +156 -0
  57. westpa/core/kinetics/rate_averaging.py +266 -0
  58. westpa/core/progress.py +218 -0
  59. westpa/core/propagators/__init__.py +54 -0
  60. westpa/core/propagators/executable.py +715 -0
  61. westpa/core/reweight/__init__.py +14 -0
  62. westpa/core/reweight/_reweight.cpython-312-darwin.so +0 -0
  63. westpa/core/reweight/matrix.py +126 -0
  64. westpa/core/segment.py +119 -0
  65. westpa/core/sim_manager.py +830 -0
  66. westpa/core/states.py +359 -0
  67. westpa/core/systems.py +93 -0
  68. westpa/core/textio.py +74 -0
  69. westpa/core/trajectory.py +330 -0
  70. westpa/core/we_driver.py +908 -0
  71. westpa/core/wm_ops.py +43 -0
  72. westpa/core/yamlcfg.py +391 -0
  73. westpa/fasthist/__init__.py +34 -0
  74. westpa/fasthist/__main__.py +110 -0
  75. westpa/fasthist/_fasthist.cpython-312-darwin.so +0 -0
  76. westpa/mclib/__init__.py +264 -0
  77. westpa/mclib/__main__.py +28 -0
  78. westpa/mclib/_mclib.cpython-312-darwin.so +0 -0
  79. westpa/oldtools/__init__.py +4 -0
  80. westpa/oldtools/aframe/__init__.py +35 -0
  81. westpa/oldtools/aframe/atool.py +75 -0
  82. westpa/oldtools/aframe/base_mixin.py +26 -0
  83. westpa/oldtools/aframe/binning.py +178 -0
  84. westpa/oldtools/aframe/data_reader.py +560 -0
  85. westpa/oldtools/aframe/iter_range.py +200 -0
  86. westpa/oldtools/aframe/kinetics.py +117 -0
  87. westpa/oldtools/aframe/mcbs.py +146 -0
  88. westpa/oldtools/aframe/output.py +39 -0
  89. westpa/oldtools/aframe/plotting.py +90 -0
  90. westpa/oldtools/aframe/trajwalker.py +126 -0
  91. westpa/oldtools/aframe/transitions.py +469 -0
  92. westpa/oldtools/cmds/__init__.py +0 -0
  93. westpa/oldtools/cmds/w_ttimes.py +358 -0
  94. westpa/oldtools/files.py +34 -0
  95. westpa/oldtools/miscfn.py +23 -0
  96. westpa/oldtools/stats/__init__.py +4 -0
  97. westpa/oldtools/stats/accumulator.py +35 -0
  98. westpa/oldtools/stats/edfs.py +129 -0
  99. westpa/oldtools/stats/mcbs.py +89 -0
  100. westpa/tools/__init__.py +33 -0
  101. westpa/tools/binning.py +472 -0
  102. westpa/tools/core.py +340 -0
  103. westpa/tools/data_reader.py +159 -0
  104. westpa/tools/dtypes.py +31 -0
  105. westpa/tools/iter_range.py +198 -0
  106. westpa/tools/kinetics_tool.py +340 -0
  107. westpa/tools/plot.py +283 -0
  108. westpa/tools/progress.py +17 -0
  109. westpa/tools/selected_segs.py +154 -0
  110. westpa/tools/wipi.py +751 -0
  111. westpa/trajtree/__init__.py +4 -0
  112. westpa/trajtree/_trajtree.cpython-312-darwin.so +0 -0
  113. westpa/trajtree/trajtree.py +117 -0
  114. westpa/westext/__init__.py +0 -0
  115. westpa/westext/adaptvoronoi/__init__.py +3 -0
  116. westpa/westext/adaptvoronoi/adaptVor_driver.py +214 -0
  117. westpa/westext/hamsm_restarting/__init__.py +3 -0
  118. westpa/westext/hamsm_restarting/example_overrides.py +35 -0
  119. westpa/westext/hamsm_restarting/restart_driver.py +1165 -0
  120. westpa/westext/stringmethod/__init__.py +11 -0
  121. westpa/westext/stringmethod/fourier_fitting.py +69 -0
  122. westpa/westext/stringmethod/string_driver.py +253 -0
  123. westpa/westext/stringmethod/string_method.py +306 -0
  124. westpa/westext/weed/BinCluster.py +180 -0
  125. westpa/westext/weed/ProbAdjustEquil.py +100 -0
  126. westpa/westext/weed/UncertMath.py +247 -0
  127. westpa/westext/weed/__init__.py +10 -0
  128. westpa/westext/weed/weed_driver.py +182 -0
  129. westpa/westext/wess/ProbAdjust.py +101 -0
  130. westpa/westext/wess/__init__.py +6 -0
  131. westpa/westext/wess/wess_driver.py +207 -0
  132. westpa/work_managers/__init__.py +57 -0
  133. westpa/work_managers/core.py +396 -0
  134. westpa/work_managers/environment.py +134 -0
  135. westpa/work_managers/mpi.py +318 -0
  136. westpa/work_managers/processes.py +187 -0
  137. westpa/work_managers/serial.py +28 -0
  138. westpa/work_managers/threads.py +79 -0
  139. westpa/work_managers/zeromq/__init__.py +20 -0
  140. westpa/work_managers/zeromq/core.py +641 -0
  141. westpa/work_managers/zeromq/node.py +131 -0
  142. westpa/work_managers/zeromq/work_manager.py +526 -0
  143. westpa/work_managers/zeromq/worker.py +320 -0
  144. westpa-2022.10.dist-info/AUTHORS +22 -0
  145. westpa-2022.10.dist-info/LICENSE +21 -0
  146. westpa-2022.10.dist-info/METADATA +183 -0
  147. westpa-2022.10.dist-info/RECORD +150 -0
  148. westpa-2022.10.dist-info/WHEEL +5 -0
  149. westpa-2022.10.dist-info/entry_points.txt +29 -0
  150. westpa-2022.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,746 @@
1
+ import itertools
2
+ import numpy as np
3
+ import pandas as pd
4
+ import sys
5
+
6
+ from westpa.core.binning.assign import BinMapper
7
+ from westpa.core.h5io import WESTPAH5File, tostr
8
+ from westpa.core.segment import Segment
9
+ from westpa.core.states import BasisState, InitialState, TargetState
10
+ from westpa.tools.binning import mapper_from_hdf5
11
+
12
+
13
+ class Run:
14
+ """A read-only view of a WESTPA simulation run.
15
+
16
+ Parameters
17
+ ----------
18
+ h5filename : str or file-like object, default 'west.h5'
19
+ Pathname or stream of a main WESTPA HDF5 data file.
20
+
21
+ """
22
+
23
+ DESCRIPTION = 'WESTPA Run'
24
+
25
+ def __init__(self, h5filename='west.h5'):
26
+ self.h5filename = h5filename
27
+
28
+ def __enter__(self):
29
+ return self
30
+
31
+ def __exit__(self, exc_type, exc_val, traceback):
32
+ self.close()
33
+
34
+ @classmethod
35
+ def open(cls, h5filename='west.h5'):
36
+ """Alternate constructor.
37
+
38
+ Parameters
39
+ ----------
40
+ h5filename : str or file-like object, default 'west.h5'
41
+ Pathname or stream of a main WESTPA HDF5 data file.
42
+
43
+ """
44
+ return cls(h5filename)
45
+
46
+ def close(self):
47
+ """Close the Run instance by closing the underlying WESTPA HDF5 file."""
48
+ self.h5file.close()
49
+
50
+ @property
51
+ def closed(self):
52
+ """bool: Whether the Run instance is closed."""
53
+ return not bool(self.h5file)
54
+
55
+ @property
56
+ def h5filename(self):
57
+ return self.h5file.filename
58
+
59
+ @h5filename.setter
60
+ def h5filename(self, value):
61
+ try:
62
+ h5file = WESTPAH5File(value, 'r')
63
+ except FileNotFoundError as e:
64
+ e.strerror = f'Failed to open {self.DESCRIPTION}: file {value!r} not found'
65
+ raise e.with_traceback(None)
66
+ self.h5file = h5file
67
+
68
+ @property
69
+ def summary(self):
70
+ """pd.DataFrame: Summary data by iteration."""
71
+ df = pd.DataFrame(
72
+ self.h5file['summary'][: self.num_iterations],
73
+ index=range(1, self.num_iterations + 1),
74
+ dtype=object,
75
+ )
76
+ df.pop('norm') # should always be 1.0
77
+ df.pop('binhash') # not human readable
78
+ return df
79
+
80
+ @property
81
+ def num_iterations(self):
82
+ """int: Number of completed iterations."""
83
+ if not hasattr(self, '_num_iterations'):
84
+ current = self.h5file.attrs['west_current_iteration']
85
+ grp = self.h5file.get_iter_group(current)
86
+ if (grp['seg_index']['status'] == Segment.SEG_STATUS_COMPLETE).all():
87
+ self._num_iterations = current
88
+ else:
89
+ self._num_iterations = current - 1
90
+ return self._num_iterations
91
+
92
+ @property
93
+ def iterations(self):
94
+ """Sequence[Iteration]: Sequence of iterations."""
95
+ return [Iteration(number, self) for number in range(1, self.num_iterations + 1)]
96
+
97
+ @property
98
+ def num_walkers(self):
99
+ """int: Total number of walkers."""
100
+ return sum(iteration.num_segments for iteration in self)
101
+
102
+ @property
103
+ def num_segments(self):
104
+ """int: Total number of trajectory segments (alias self.num_walkers)."""
105
+ return self.num_walkers
106
+
107
+ @property
108
+ def walkers(self):
109
+ """Iterable[Walker]: All walkers in the run."""
110
+ return itertools.chain.from_iterable(self)
111
+
112
+ @property
113
+ def recycled_walkers(self):
114
+ """Iterable[Walker]: Walkers that stopped in the sink."""
115
+ return itertools.chain.from_iterable(iteration.recycled_walkers for iteration in self)
116
+
117
+ @property
118
+ def initial_walkers(self):
119
+ """Iterable[Walker]: Walkers whose parents are initial states."""
120
+ return itertools.chain.from_iterable(iteration.initial_walkers for iteration in self)
121
+
122
+ def iteration(self, number):
123
+ """Return a specific iteration.
124
+
125
+ Parameters
126
+ ----------
127
+ number : int
128
+ Iteration number (1-based).
129
+
130
+ Returns
131
+ -------
132
+ Iteration
133
+ The iteration indexed by `number`.
134
+
135
+ """
136
+ valid_range = range(1, self.num_iterations + 1)
137
+ if number not in valid_range:
138
+ raise ValueError(f'iteration number must be in {valid_range}')
139
+ return Iteration(number, self)
140
+
141
+ def __len__(self):
142
+ return self.num_iterations
143
+
144
+ def __iter__(self):
145
+ return iter(self.iterations)
146
+
147
+ def __contains__(self, iteration):
148
+ return iteration.run == self
149
+
150
+ def __eq__(self, other):
151
+ return self.h5file == other.h5file
152
+
153
+ def __hash__(self):
154
+ return hash(self.h5file)
155
+
156
+ def __bool__(self):
157
+ return not self.closed
158
+
159
+ def __repr__(self):
160
+ if self.closed:
161
+ return f'<Closed {self.DESCRIPTION} at {hex(id(self))}>'
162
+ return f'<{self.DESCRIPTION} with {self.num_iterations} iterations at {hex(id(self))}>'
163
+
164
+
165
+ class Iteration:
166
+ """An iteration of a WESTPA simulation.
167
+
168
+ Parameters
169
+ ----------
170
+ number : int
171
+ Iteration number (1-based).
172
+ run : Run
173
+ Simulation run to which the iteration belongs.
174
+
175
+ """
176
+
177
+ def __init__(self, number, run):
178
+ self.number = number
179
+ self.run = run
180
+
181
+ @property
182
+ def h5group(self):
183
+ """h5py.Group: HDF5 group containing the iteration data."""
184
+ return self.run.h5file.get_iter_group(self.number)
185
+
186
+ @property
187
+ def prev(self):
188
+ """Iteration: Previous iteration."""
189
+ if self.number == 1:
190
+ return None
191
+ return self.run.iteration(self.number - 1)
192
+
193
+ @property
194
+ def next(self):
195
+ """Iteration: Next iteration."""
196
+ if self.number == self.run.num_iterations:
197
+ return None
198
+ return self.run.iteration(self.number + 1)
199
+
200
+ @property
201
+ def summary(self):
202
+ """pd.DataFrame: Iteration summary."""
203
+ df = pd.DataFrame(
204
+ self.run.h5file['summary'][[self.number - 1]],
205
+ index=[self.number],
206
+ dtype=object,
207
+ )
208
+ df.pop('norm') # should always be 1.0
209
+ df.pop('binhash') # not human readable
210
+ return df.iloc[0]
211
+
212
+ @property
213
+ def segment_summaries(self):
214
+ """pd.DataFrame: Segment summary data for the iteration."""
215
+ df = pd.DataFrame(self.h5group['seg_index'][:], dtype=object)
216
+
217
+ # Make 'endpoint_type' and 'status' human-readable.
218
+ names = map(Segment.endpoint_type_names.get, df['endpoint_type'])
219
+ df['endpoint_type'] = [name.split('_')[-1] for name in names]
220
+ names = map(Segment.status_names.get, df['status'])
221
+ df['status'] = [name.split('_')[-1] for name in names]
222
+
223
+ return df
224
+
225
+ @property
226
+ def pcoords(self):
227
+ """3D ndarray: Progress coordinate snaphots of each walker."""
228
+ return self.h5group['pcoord'][:]
229
+
230
+ @property
231
+ def weights(self):
232
+ """1D ndarray: Statistical weight of each walker."""
233
+ return self.h5group['seg_index']['weight']
234
+
235
+ @property
236
+ def bin_target_counts(self):
237
+ """1D ndarray, dtype=uint64: Target count for each bin."""
238
+ val = self.h5group.get('bin_target_counts')
239
+ if val is None:
240
+ return None
241
+ return val[:]
242
+
243
+ @property
244
+ def bin_mapper(self):
245
+ """BinMapper: Bin mapper used in the iteration."""
246
+ if self.bin_target_counts is None:
247
+ return None
248
+ mapper, _, _ = mapper_from_hdf5(self.run.h5file['bin_topologies'], self.h5group.attrs['binhash'])
249
+ return mapper
250
+
251
+ @property
252
+ def num_bins(self):
253
+ """int: Number of bins."""
254
+ if self.number == 1:
255
+ return 1
256
+ return self.bin_target_counts.shape[0]
257
+
258
+ @property
259
+ def bins(self):
260
+ """Iterable[Bin]: Bins."""
261
+ mapper = self.bin_mapper
262
+ return (Bin(index, mapper) for index in range(self.num_bins))
263
+
264
+ @property
265
+ def num_walkers(self):
266
+ """int: Number of walkers in the iteration."""
267
+ return self.h5group['seg_index'].shape[0]
268
+
269
+ @property
270
+ def num_segments(self):
271
+ """int: Number of trajectory segments (alias self.num_walkers)."""
272
+ return self.num_walkers
273
+
274
+ @property
275
+ def walkers(self):
276
+ """Iterable[Walker]: Walkers in the iteration."""
277
+ return (Walker(index, self) for index in range(self.num_walkers))
278
+
279
+ @property
280
+ def recycled_walkers(self):
281
+ """Iterable[Walker]: Walkers that stopped in the sink."""
282
+ endpoint_type = self.h5group['seg_index']['endpoint_type']
283
+ indices = np.flatnonzero(endpoint_type == Segment.SEG_ENDPOINT_RECYCLED)
284
+ return (Walker(index, self) for index in indices)
285
+
286
+ @property
287
+ def initial_walkers(self):
288
+ """Iterable[Walker]: Walkers whose parents are initial states."""
289
+ parent_ids = self.h5group['seg_index']['parent_id']
290
+ return (walker for walker, parent_id in zip(self, parent_ids) if parent_id < 0)
291
+
292
+ @property
293
+ def auxiliary_data(self):
294
+ """h5py.Group or None: Auxiliary data stored for the iteration."""
295
+ return self.h5group.get('auxdata')
296
+
297
+ @property
298
+ def basis_state_summaries(self):
299
+ """pd.DataFrame: Basis state summary data."""
300
+ df = pd.DataFrame(self.h5group['ibstates']['bstate_index'][:])
301
+ df['label'] = df['label'].str.decode('UTF-8')
302
+ df['auxref'] = df['auxref'].str.decode('UTF-8')
303
+ return df
304
+
305
+ @property
306
+ def basis_state_pcoords(self):
307
+ """2D ndarray: Progress coordinates of each basis state."""
308
+ return self.h5group['ibstates']['bstate_pcoord'][:]
309
+
310
+ @property
311
+ def basis_states(self):
312
+ """list[BasisState]: Basis states in use for the iteration."""
313
+ return [
314
+ BasisState(row.label, row.probability, pcoord=pcoord, auxref=row.auxref, state_id=row.Index)
315
+ for row, pcoord in zip(self.basis_state_summaries.itertuples(), self.basis_state_pcoords)
316
+ ]
317
+
318
+ @property
319
+ def has_target_states(self):
320
+ """bool: Whether target (sink) states are defined for this iteration."""
321
+ return 'tstates' in self.h5group
322
+
323
+ @property
324
+ def target_state_summaries(self):
325
+ """pd.DataFrame or None: Target state summary data."""
326
+ if self.has_target_states:
327
+ df = pd.DataFrame(self.h5group['tstates']['index'][:])
328
+ df['label'] = df['label'].str.decode('UTF-8')
329
+
330
+ return df
331
+ else:
332
+ return None
333
+
334
+ @property
335
+ def target_state_pcoords(self):
336
+ """2D ndarray or None: Progress coordinates of each target state."""
337
+ return self.h5group['tstates']['pcoord'][:] if self.has_target_states else None
338
+
339
+ @property
340
+ def target_states(self):
341
+ """list[TargetState]: Target states in use for the iteration."""
342
+ if not self.has_target_states:
343
+ return []
344
+ return [
345
+ TargetState(row.label, pcoord, state_id=row.Index)
346
+ for row, pcoord in zip(self.target_state_summaries.itertuples(), self.target_state_pcoords)
347
+ ]
348
+
349
+ @property
350
+ def sink(self):
351
+ """BinUnion or None: Union of bins serving as the recycling sink."""
352
+ if not self.has_target_states:
353
+ return None
354
+ mapper = Iteration(self.number + 1, self.run).bin_mapper
355
+ return BinUnion(mapper.assign(self.target_state_pcoords), mapper)
356
+
357
+ def bin(self, index):
358
+ """Return the bin with the given index.
359
+
360
+ Parameters
361
+ ----------
362
+ index : int
363
+ Bin index (0-based).
364
+
365
+ Returns
366
+ -------
367
+ Bin
368
+ The bin indexed by `index`.
369
+
370
+ """
371
+ return Bin(index, self.bin_mapper)
372
+
373
+ def walker(self, index):
374
+ """Return the walker with the given index.
375
+
376
+ Parameters
377
+ ----------
378
+ index : int
379
+ Walker index (0-based).
380
+
381
+ Returns
382
+ -------
383
+ Walker
384
+ The walker indexed by `index`.
385
+
386
+ """
387
+ valid_range = range(self.num_walkers)
388
+ if index not in valid_range:
389
+ raise ValueError(f'walker index must be in {valid_range}')
390
+ return Walker(index, self)
391
+
392
+ def basis_state(self, index):
393
+ """Return the basis state with the given index.
394
+
395
+ Parameters
396
+ ----------
397
+ index : int
398
+ Basis state index (0-based).
399
+
400
+ Returns
401
+ -------
402
+ BasisState
403
+ The basis state indexed by `index`.
404
+
405
+ """
406
+ row = self.h5group['ibstates']['bstate_index'][index]
407
+ pcoord = self.h5group['ibstates']['bstate_pcoord'][index]
408
+ return BasisState(
409
+ tostr(row['label']),
410
+ row['probability'],
411
+ pcoord=pcoord,
412
+ auxref=tostr(row['auxref']),
413
+ state_id=index,
414
+ )
415
+
416
+ def target_state(self, index):
417
+ """Return the target state with the given index.
418
+
419
+ Parameters
420
+ ----------
421
+ index : int
422
+ Target state index (0-based).
423
+
424
+ Returns
425
+ -------
426
+ TargetState
427
+ The target state indexed by `index`.
428
+
429
+ """
430
+ row = self.h5group['tstates']['index'][index]
431
+ pcoord = self.h5group['tstates']['pcoord'][index]
432
+ return TargetState(tostr(row['label']), pcoord, state_id=index)
433
+
434
+ def __iter__(self):
435
+ return iter(self.walkers)
436
+
437
+ def __contains__(self, walker):
438
+ return walker.iteration == self
439
+
440
+ def __eq__(self, other):
441
+ return self.number == other.number and self.run == other.run
442
+
443
+ def __hash__(self):
444
+ return hash((self.number, self.run))
445
+
446
+ def __repr__(self):
447
+ return f'{self.__class__.__name__}({self.number}, {self.run})'
448
+
449
+
450
+ class Walker:
451
+ """A walker in an iteration of a WESTPA simulation.
452
+
453
+ Parameters
454
+ ----------
455
+ index : int
456
+ Walker index (0-based).
457
+ iteration : Iteration
458
+ Iteration to which the walker belongs.
459
+
460
+ """
461
+
462
+ def __init__(self, index, iteration):
463
+ self.index = index
464
+ self.iteration = iteration
465
+
466
+ @property
467
+ def run(self):
468
+ """Run: Run to which the walker belongs."""
469
+ return self.iteration.run
470
+
471
+ @property
472
+ def weight(self):
473
+ """float64: Statistical weight of the walker."""
474
+ return self.iteration.weights[self.index]
475
+
476
+ @property
477
+ def pcoords(self):
478
+ """2D ndarray: Progress coordinate snapshots."""
479
+ return self.iteration.h5group['pcoord'][self.index]
480
+
481
+ @property
482
+ def num_snapshots(self):
483
+ """int: Number of snapshots."""
484
+ return self.pcoords.shape[0]
485
+
486
+ @property
487
+ def segment_summary(self):
488
+ """pd.Series: Segment summary data."""
489
+ df = pd.DataFrame(
490
+ self.iteration.h5group['seg_index'][[self.index]],
491
+ index=[self.index],
492
+ dtype=object,
493
+ )
494
+
495
+ # Make 'endpoint_type' and 'status' human-readable.
496
+ names = map(Segment.endpoint_type_names.get, df['endpoint_type'])
497
+ df['endpoint_type'] = [name.split('_')[-1] for name in names]
498
+ names = map(Segment.status_names.get, df['status'])
499
+ df['status'] = [name.split('_')[-1] for name in names]
500
+
501
+ return df.iloc[0]
502
+
503
+ @property
504
+ def parent(self):
505
+ """Walker or InitialState: The parent of the walker."""
506
+ parent_id = self.iteration.h5group['seg_index']['parent_id'][self.index]
507
+
508
+ if parent_id >= 0:
509
+ return Walker(parent_id, self.iteration.prev)
510
+
511
+ istate_id = -(parent_id + 1)
512
+ row = self.iteration.h5group['ibstates']['istate_index'][istate_id]
513
+
514
+ # Initial states may or may not be generated from a basis state.
515
+ bstate_id = row['basis_state_id']
516
+ try:
517
+ bstate = self.iteration.basis_state(bstate_id)
518
+ except IndexError:
519
+ bstate = None
520
+ bstate_id = None
521
+
522
+ return InitialState(
523
+ istate_id,
524
+ bstate_id,
525
+ row['iter_created'],
526
+ iter_used=row['iter_used'],
527
+ istate_type=row['istate_type'],
528
+ istate_status=row['istate_status'],
529
+ pcoord=self.iteration.h5group['ibstates']['istate_pcoord'][istate_id],
530
+ basis_state=bstate,
531
+ )
532
+
533
+ @property
534
+ def children(self):
535
+ """Iterable[Walker]: The children of the walker."""
536
+ next = self.iteration.next
537
+ if next is None:
538
+ return ()
539
+ indices = np.flatnonzero(next.h5group['seg_index']['parent_id'] == self.index)
540
+ return (Walker(index, next) for index in indices)
541
+
542
+ @property
543
+ def recycled(self):
544
+ """bool: True if the walker stopped in the sink, False otherwise."""
545
+ endpoint_type = self.iteration.h5group['seg_index']['endpoint_type'][self.index]
546
+ return endpoint_type == Segment.SEG_ENDPOINT_RECYCLED
547
+
548
+ @property
549
+ def initial(self):
550
+ """bool: True if the parent of the walker is an initial state, False otherwise."""
551
+ return self.iteration.h5group['seg_index']['parent_id'][self.index] < 0
552
+
553
+ @property
554
+ def auxiliary_data(self):
555
+ """dict: Auxiliary data for the walker."""
556
+ data = self.iteration.auxiliary_data or {}
557
+ return {name: data[name][self.index] for name in data}
558
+
559
+ def trace(self, **kwargs):
560
+ """Return the trace (ancestral line) of the walker.
561
+
562
+ For full documentation see :class:`Trace`.
563
+
564
+ Returns
565
+ -------
566
+ Trace
567
+ The trace of the walker.
568
+
569
+ """
570
+ return Trace(self, **kwargs)
571
+
572
+ def __eq__(self, other):
573
+ return self.index == other.index and self.iteration == other.iteration
574
+
575
+ def __hash__(self):
576
+ return hash((self.index, self.iteration))
577
+
578
+ def __repr__(self):
579
+ return f'{self.__class__.__name__}({self.index}, {self.iteration})'
580
+
581
+
582
+ class BinUnion:
583
+ """A (disjoint) union of bins defined by a common bin mapper.
584
+
585
+ Parameters
586
+ ----------
587
+ indices : iterable of int
588
+ The indices of the bins comprising the union.
589
+ mapper : BinMapper
590
+ The bin mapper defining the bins.
591
+
592
+ """
593
+
594
+ def __init__(self, indices, mapper):
595
+ if not isinstance(mapper, BinMapper):
596
+ raise TypeError(f'mapper must be an instance of {BinMapper}')
597
+
598
+ indices = set(indices)
599
+ valid_range = range(mapper.nbins)
600
+ if any(index not in valid_range for index in indices):
601
+ raise ValueError(f'bin indices must be in {valid_range}')
602
+
603
+ self.indices = indices
604
+ self.mapper = mapper
605
+
606
+ def union(self, *others):
607
+ """Return the union of the bin union and all others.
608
+
609
+ Parameters
610
+ ----------
611
+ *others : BinUnion
612
+ Other :class:`BinUnion` instances, consisting of bins defined by
613
+ the same underlying bin mapper.
614
+
615
+ Returns
616
+ -------
617
+ BinUnion
618
+ The union of `self` and `others`.
619
+
620
+ """
621
+ if any(other.mapper != self.mapper for other in others):
622
+ raise ValueError('bins must be defined by the same bin mapper')
623
+ indices = self.indices.union(*(other.indices for other in others))
624
+ return BinUnion(indices, self.mapper)
625
+
626
+ def intersection(self, *others):
627
+ """Return the intersection of the bin union and all others.
628
+
629
+ Parameters
630
+ ----------
631
+ *others : BinUnion
632
+ Other :class:`BinUnion` instances, consisting of bins defined by
633
+ the same underlying bin mapper.
634
+
635
+ Returns
636
+ -------
637
+ BinUnion
638
+ The itersection of `self` and `others`.
639
+
640
+ """
641
+ if any(other.mapper != self.mapper for other in others):
642
+ raise ValueError('bins must be defined by the same bin mapper')
643
+ indices = self.indices.intersection(*(other.indices for other in others))
644
+ return BinUnion(indices, self.mapper)
645
+
646
+ def __contains__(self, coord):
647
+ result = self.mapper.assign([coord])
648
+ if result.size != 1:
649
+ raise ValueError('left operand must be a single coordinate tuple')
650
+ return result[0] in self.indices
651
+
652
+ def __or__(self, other):
653
+ return self.union(other)
654
+
655
+ def __and__(self, other):
656
+ return self.intersection(other)
657
+
658
+ def __bool__(self):
659
+ return bool(self.indices)
660
+
661
+ def __repr__(self):
662
+ return f'{self.__class__.__name__}({self.indices}, {self.mapper})'
663
+
664
+
665
+ class Bin(BinUnion):
666
+ """A bin defined by a bin mapper.
667
+
668
+ Parameters
669
+ ----------
670
+ index : int
671
+ The index of the bin.
672
+ mapper : BinMapper
673
+ The bin mapper defining the bin.
674
+
675
+ """
676
+
677
+ def __init__(self, index, mapper):
678
+ super().__init__({index}, mapper)
679
+ self.index = index
680
+
681
+ def __repr__(self):
682
+ return f'{self.__class__.__name__}({self.index}, {self.mapper})'
683
+
684
+
685
+ class Trace:
686
+ """A trace of a walker's ancestry.
687
+
688
+ Parameters
689
+ ----------
690
+ walker : Walker
691
+ The terminal walker.
692
+ source : Bin, BinUnion, or collections.abc.Container, optional
693
+ A source (macro)state, specified as a container object whose
694
+ :meth:`__contains__` method is the indicator function for the
695
+ corresponding subset of progress coordinate space. The trace is
696
+ stopped upon encountering a walker that stopped in `source`.
697
+ max_length : int, optional
698
+ The maximum number of walkers in the trace.
699
+
700
+ """
701
+
702
+ def __init__(self, walker, source=None, max_length=None):
703
+ if max_length is None:
704
+ max_length = sys.maxsize
705
+ else:
706
+ max_length = int(max_length)
707
+ if max_length < 1:
708
+ raise ValueError('max_length must be at least 1')
709
+
710
+ walkers = []
711
+ initial_state = None
712
+ while len(walkers) < max_length:
713
+ if source and walker.pcoords[-1] in source:
714
+ break
715
+ walkers.append(walker)
716
+ parent = walker.parent
717
+ if isinstance(parent, InitialState):
718
+ initial_state = parent
719
+ break
720
+ walker = parent
721
+ walkers.reverse()
722
+
723
+ self.walkers = walkers
724
+ self.initial_state = initial_state
725
+ self.source = source
726
+ self.max_length = max_length
727
+
728
+ def __len__(self):
729
+ return len(self.walkers)
730
+
731
+ def __iter__(self):
732
+ return iter(self.walkers)
733
+
734
+ def __contains__(self, walker):
735
+ return walker in self.walkers
736
+
737
+ def __getitem__(self, key):
738
+ return self.walkers[key]
739
+
740
+ def __repr__(self):
741
+ s = f'Trace({self.walkers[-1]}'
742
+ if self.source:
743
+ s += f', source={self.source}'
744
+ if self.max_length < sys.maxsize:
745
+ s += f', max_length={self.max_length}'
746
+ return s + ')'