westpa 2022.10__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_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-x86_64-linux-gnu.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-x86_64-linux-gnu.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-x86_64-linux-gnu.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-x86_64-linux-gnu.so +0 -0
  76. westpa/mclib/__init__.py +264 -0
  77. westpa/mclib/__main__.py +28 -0
  78. westpa/mclib/_mclib.cpython-312-x86_64-linux-gnu.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-x86_64-linux-gnu.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 +6 -0
  149. westpa-2022.10.dist-info/entry_points.txt +29 -0
  150. westpa-2022.10.dist-info/top_level.txt +1 -0
westpa/core/wm_ops.py ADDED
@@ -0,0 +1,43 @@
1
+ import westpa
2
+
3
+ import logging
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+
8
+ def get_pcoord(state):
9
+ log.debug('getting progress coordinate for {!r}'.format(state))
10
+ propagator = westpa.rc.get_propagator()
11
+ propagator.get_pcoord(state)
12
+ return state
13
+
14
+
15
+ def gen_istate(basis_state, initial_state):
16
+ log.debug('generating initial state from {!r} (into {!r})'.format(basis_state, initial_state))
17
+ propagator = westpa.rc.get_propagator()
18
+ propagator.update_basis_initial_states([basis_state], [initial_state])
19
+ propagator.gen_istate(basis_state, initial_state)
20
+ return basis_state, initial_state
21
+
22
+
23
+ def prep_iter(n_iter, segments):
24
+ log.debug('propagator.prepare_iteration(...)')
25
+ propagator = westpa.rc.get_propagator()
26
+ propagator.clear_basis_initial_states()
27
+ propagator.prepare_iteration(n_iter, segments)
28
+
29
+
30
+ def post_iter(n_iter, segments):
31
+ log.debug('propagator.finalize_iteration(...)')
32
+ propagator = westpa.rc.get_propagator()
33
+ propagator.finalize_iteration(n_iter, segments)
34
+
35
+
36
+ def propagate(basis_states, initial_states, segments):
37
+ propagator = westpa.rc.get_propagator()
38
+ propagator.update_basis_initial_states(basis_states, initial_states)
39
+ outgoing_ids = [segment.seg_id for segment in segments]
40
+ incoming_segments = {segment.seg_id: segment for segment in propagator.propagate(segments)}
41
+ if log.isEnabledFor(logging.DEBUG):
42
+ log.debug('propagated {:d} segments'.format(len(incoming_segments)))
43
+ return [incoming_segments[seg_id] for seg_id in outgoing_ids]
westpa/core/yamlcfg.py ADDED
@@ -0,0 +1,391 @@
1
+ '''
2
+ YAML-based configuration files for WESTPA
3
+ '''
4
+
5
+ import os
6
+ import warnings
7
+
8
+ import yaml
9
+ import numpy as np
10
+
11
+ try:
12
+ from yaml import CLoader as YLoader
13
+ except ImportError:
14
+ # fall back on Python implementation
15
+ from yaml import Loader as YLoader
16
+
17
+ from . import extloader
18
+ from .binning import NopMapper
19
+
20
+ # Only needed for temporary class
21
+ import westpa
22
+
23
+ NotProvided = object()
24
+
25
+
26
+ class ConfigValueWarning(UserWarning):
27
+ pass
28
+
29
+
30
+ def warn_dubious_config_entry(entry, value, expected_type=None, category=ConfigValueWarning, stacklevel=1):
31
+ if expected_type:
32
+ warnings.warn(
33
+ 'dubious configuration entry {}: {} (expected type {})'.format(entry, value, expected_type), category, stacklevel + 1
34
+ )
35
+ else:
36
+ warnings.warn('dubious configuration entry {}: {}'.format(entry, value), category, stacklevel + 1)
37
+
38
+
39
+ def check_bool(value, action='warn'):
40
+ '''Check that the given ``value`` is boolean in type. If not, either
41
+ raise a warning (if ``action=='warn'``) or an exception (``action=='raise'``).
42
+ '''
43
+ if action not in ('warn', 'raise'):
44
+ raise ValueError('invalid action {!r}'.format(action))
45
+
46
+ if not isinstance(value, bool):
47
+ if action == 'warn':
48
+ warnings.warn(
49
+ 'dubious boolean value {!r}, will be treated as {!r}'.format(value, bool(value)),
50
+ category=ConfigValueWarning,
51
+ stacklevel=2,
52
+ )
53
+ elif action == 'raise':
54
+ raise ValueError('dubious boolean value {!r}, would be treated as {!r}'.format(value, bool(value)))
55
+ else:
56
+ return value
57
+
58
+
59
+ class ConfigItemMissing(KeyError):
60
+ def __init__(self, key, message=None):
61
+ self.key = key
62
+ if message is None:
63
+ message = 'configuration item missing: {!r}'.format(key)
64
+ super().__init__(message)
65
+
66
+
67
+ class ConfigItemTypeError(TypeError):
68
+ def __init__(self, key, expected_type, message=None):
69
+ self.key = key
70
+ self.expected_type = expected_type
71
+ if message is None:
72
+ message = 'configuration item {!r} must have type {!r}'.format(key, expected_type)
73
+ super().__init__(message)
74
+
75
+
76
+ class ConfigValueError(ValueError):
77
+ def __init__(self, key, value, message=None):
78
+ self.key = key
79
+ self.value = value
80
+ if message is None:
81
+ message = 'bad value {!r} for configuration item {!r}'.format(key, value)
82
+ super().__init__(message)
83
+
84
+
85
+ class YAMLConfig:
86
+ preload_config_files = ['/etc/westpa/westrc', os.path.expanduser('~/.westrc')]
87
+
88
+ def __init__(self):
89
+ self._data = {}
90
+
91
+ for source in self.preload_config_files:
92
+ self.update_from_file(source, required=False)
93
+
94
+ def __repr__(self):
95
+ return repr(self._data)
96
+
97
+ def update_from_file(self, file, required=True):
98
+ if isinstance(file, str):
99
+ try:
100
+ file = open(file, 'rt')
101
+ except IOError:
102
+ if required:
103
+ raise
104
+ else:
105
+ return
106
+
107
+ self._data.update(yaml.load(file, Loader=YLoader))
108
+ file.close()
109
+
110
+ def _normalize_key(self, key):
111
+ if isinstance(key, str):
112
+ key = (key,)
113
+ else:
114
+ try:
115
+ key = tuple(key)
116
+ except TypeError:
117
+ key = (key,)
118
+ return key
119
+
120
+ def _resolve_object_chain(self, key, last=None):
121
+ if last is None:
122
+ last = len(key)
123
+ objects = [self._data[key[0]]]
124
+ for subkey in key[1:last]:
125
+ objects.append(objects[-1][subkey])
126
+ return objects
127
+
128
+ def __getitem__(self, key):
129
+ key = self._normalize_key(key)
130
+ return self._resolve_object_chain(key)[-1]
131
+
132
+ def __setitem__(self, key, value):
133
+ key = self._normalize_key(key)
134
+
135
+ try:
136
+ objchain = self._resolve_object_chain(key, -1)
137
+ except KeyError:
138
+ # creation of a new (possibly nested) entry
139
+ val = self._data
140
+ for keypart in key[:-1]:
141
+ try:
142
+ val = val[keypart]
143
+ except KeyError:
144
+ val[keypart] = {}
145
+ val = val[keypart]
146
+ try:
147
+ val = val[key[-1]]
148
+ except KeyError:
149
+ val[key[-1]] = value
150
+ else:
151
+ objchain[-1][key[-1]] = value
152
+
153
+ def __delitem__(self, key):
154
+ key = self._normalize_key(key)
155
+ objchain = self._resolve_object_chain(key, -1)
156
+ del objchain[-1][key[-1]]
157
+
158
+ def __contains__(self, key):
159
+ try:
160
+ self[key]
161
+ except (KeyError, TypeError):
162
+ return False
163
+ else:
164
+ return True
165
+
166
+ def require(self, key, type_=None):
167
+ '''Ensure that a configuration item with the given ``key`` is present. If
168
+ the optional ``type_`` is given, additionally require that the item has that
169
+ type.'''
170
+
171
+ try:
172
+ item = self[key]
173
+ except KeyError:
174
+ raise ConfigItemMissing(key)
175
+
176
+ if type_ is not None:
177
+ if not isinstance(item, type_):
178
+ raise ConfigItemTypeError(item, type_)
179
+ return item
180
+
181
+ def require_type_if_present(self, key, type_):
182
+ '''Ensure that the configuration item with the given ``key`` has the
183
+ given type.'''
184
+
185
+ try:
186
+ item = self[key]
187
+ except KeyError:
188
+ return
189
+ else:
190
+ if not isinstance(item, type_):
191
+ raise ConfigItemTypeError(item, type_)
192
+
193
+ def coerce_type_if_present(self, key, type_):
194
+ try:
195
+ item = self[key]
196
+ except KeyError:
197
+ return
198
+ else:
199
+ if type_ is bool and not isinstance(item, bool):
200
+ warn_dubious_config_entry(key, item, bool)
201
+ self[key] = type_(item)
202
+
203
+ def get(self, key, default=None):
204
+ try:
205
+ return self[key]
206
+ except KeyError:
207
+ return default
208
+
209
+ def get_typed(self, key, type_, default=NotProvided):
210
+ try:
211
+ item = self[key]
212
+ except KeyError as ke:
213
+ if default is not NotProvided:
214
+ item = default
215
+ else:
216
+ raise ke
217
+
218
+ # Warn about possibly bad boolean
219
+ if type_ is bool and not isinstance(item, bool):
220
+ warn_dubious_config_entry(key, item, bool)
221
+
222
+ return type_(item)
223
+
224
+ def get_path(self, key, default=NotProvided, expandvars=True, expanduser=True, realpath=True, abspath=True):
225
+ try:
226
+ path = self[key]
227
+ except KeyError as ke:
228
+ if default is not NotProvided:
229
+ path = default
230
+ else:
231
+ raise ke
232
+
233
+ if expandvars:
234
+ path = os.path.expandvars(path)
235
+ if expanduser:
236
+ path = os.path.expanduser(path)
237
+ if realpath:
238
+ path = os.path.realpath(path)
239
+ if abspath:
240
+ path = os.path.abspath(path)
241
+
242
+ return path
243
+
244
+ def get_pathlist(self, key, default=NotProvided, sep=os.pathsep, expandvars=True, expanduser=True, realpath=True, abspath=True):
245
+ try:
246
+ paths = self[key]
247
+ except KeyError as ke:
248
+ if default is not NotProvided:
249
+ paths = default
250
+ else:
251
+ raise ke
252
+
253
+ try:
254
+ items = paths.split(sep)
255
+ except AttributeError:
256
+ # Default must have been something we can't process, like a list or None
257
+ # Just pass it through, since enforcing a restriction on what kind of
258
+ # default is passed is probably more counterproductive than any poor programming
259
+ # practice it encourages.
260
+ return paths
261
+
262
+ if expandvars:
263
+ items = list(map(os.path.expandvars, items))
264
+ if expanduser:
265
+ items = list(map(os.path.expanduser, items))
266
+ if realpath:
267
+ items = list(map(os.path.realpath, items))
268
+ if abspath:
269
+ items = list(map(os.path.abspath, items))
270
+
271
+ return items
272
+
273
+ def get_python_object(self, key, default=NotProvided, path=None):
274
+ try:
275
+ qualname = self[key]
276
+ except KeyError as ke:
277
+ if default is not NotProvided:
278
+ return default
279
+ else:
280
+ raise ke
281
+
282
+ return extloader.get_object(qualname, path)
283
+
284
+ def get_choice(self, key, choices, default=NotProvided, value_transform=None):
285
+ try:
286
+ value = self[key]
287
+ except KeyError:
288
+ if default is not NotProvided:
289
+ value = default
290
+ else:
291
+ raise
292
+
293
+ choices = set(choices)
294
+ if value_transform:
295
+ value = value_transform(value)
296
+ if value not in choices:
297
+ raise ConfigValueError(
298
+ key,
299
+ value,
300
+ message='bad value {!r} for configuration item {!r} (valid choices: {!r})'.format(
301
+ value, key, tuple(sorted(choices))
302
+ ),
303
+ )
304
+ return value
305
+
306
+
307
+ # Temporary class here
308
+ class YAMLSystem:
309
+ '''A description of the system being simulated, including the dimensionality and
310
+ data type of the progress coordinate, the number of progress coordinate entries
311
+ expected from each segment, and binning. To construct a simulation, the user must
312
+ subclass WESTSystem and set several instance variables.
313
+
314
+ At a minimum, the user must subclass ``WESTSystem`` and override
315
+ :method:`initialize` to set the data type and dimensionality of progress
316
+ coordinate data and define a bin mapper.
317
+
318
+ :ivar pcoord_ndim: The number of dimensions in the progress coordinate.
319
+ Defaults to 1 (i.e. a one-dimensional progress
320
+ coordinate).
321
+ :ivar pcoord_dtype: The data type of the progress coordinate, which must be
322
+ callable (e.g. ``np.float32`` and ``long`` will work,
323
+ but ``'<f4'`` and ``'<i8'`` will not). Defaults to
324
+ ``np.float64``.
325
+ :ivar pcoord_len: The length of the progress coordinate time series
326
+ generated by each segment, including *both* the initial
327
+ and final values. Defaults to 2 (i.e. only the initial
328
+ and final progress coordinate values for a segment are
329
+ returned from propagation).
330
+ :ivar bin_mapper: A bin mapper describing the progress coordinate space.
331
+ :ivar bin_target_counts: A vector of target counts, one per bin.
332
+ '''
333
+
334
+ def __init__(self, rc=None):
335
+ self.rc = rc or westpa.rc
336
+
337
+ # Number of dimentions in progress coordinate data
338
+ self.pcoord_ndim = 1
339
+
340
+ # Length of progress coordinate data for each segment
341
+ self.pcoord_len = 2
342
+
343
+ # Data type of progress coordinate
344
+ self.pcoord_dtype = np.float32
345
+
346
+ # Mapper
347
+ self.bin_mapper = NopMapper()
348
+ # self.bin_mapper = None
349
+ self._bin_target_counts = None
350
+
351
+ self.bin_target_counts = [1]
352
+
353
+ @property
354
+ def bin_target_counts(self):
355
+ return self._bin_target_counts
356
+
357
+ @bin_target_counts.setter
358
+ def bin_target_counts(self, target_counts):
359
+ maxcount = max(target_counts)
360
+ self._bin_target_counts = np.array(target_counts, dtype=np.min_scalar_type(maxcount))
361
+
362
+ def initialize(self):
363
+ '''Prepare this system object for use in simulation or analysis,
364
+ creating a bin space, setting replicas per bin, and so on. This
365
+ function is called whenever a WEST tool creates an instance of the
366
+ system driver.
367
+ '''
368
+ pass
369
+
370
+ def prepare_run(self):
371
+ '''Prepare this system for use in a simulation run. Called by w_run in
372
+ all worker processes.'''
373
+ pass
374
+
375
+ def finalize_run(self):
376
+ '''A hook for system-specific processing for the end of a simulation run
377
+ (as defined by such things as maximum wallclock time, rather than perhaps
378
+ more scientifically-significant definitions of "the end of a simulation run")'''
379
+ pass
380
+
381
+ def new_pcoord_array(self, pcoord_len=None):
382
+ '''Return an appropriately-sized and -typed pcoord array for a timepoint, segment,
383
+ or number of segments. If ``pcoord_len`` is not specified (or None), then
384
+ a length appropriate for a segment is returned.'''
385
+
386
+ if pcoord_len is None:
387
+ pcoord_len = self.pcoord_len
388
+ return np.zeros((pcoord_len, self.pcoord_ndim), self.pcoord_dtype)
389
+
390
+ def new_region_set(self):
391
+ raise NotImplementedError('This method has been removed.')
@@ -0,0 +1,34 @@
1
+ from ._fasthist import histnd # noqa
2
+
3
+ import numpy as np
4
+
5
+
6
+ def normhistnd(hist, binbounds):
7
+ '''Normalize the N-dimensional histogram ``hist`` with corresponding
8
+ bin boundaries ``binbounds``. Modifies ``hist`` in place and returns
9
+ the normalization factor used.'''
10
+
11
+ ndim = hist.ndim
12
+
13
+ if ndim != len(binbounds):
14
+ raise ValueError(
15
+ 'shape of histogram [{!r}] does not match bin boundary sets (there are {})'.format(hist.shape, len(binbounds))
16
+ )
17
+
18
+ diffs = [np.diff(bb) for bb in binbounds]
19
+
20
+ if ndim == 1:
21
+ assert diffs[0].shape == hist.shape
22
+ normfac = (hist * diffs[0]).sum()
23
+ else:
24
+ outers = np.multiply.outer(diffs[0], diffs[1])
25
+ for delta in diffs[2:]:
26
+ outers = np.multiply.outer(outers, delta)
27
+ assert outers.shape == hist.shape, 'hist shape {} != outers shape {}'.format(hist.shape, outers.shape)
28
+ # Divide by bin volumes
29
+ hist /= outers
30
+ normfac = hist.sum()
31
+ # normfac = (hist * outers).sum()
32
+
33
+ hist /= normfac
34
+ return normfac
@@ -0,0 +1,110 @@
1
+ '''
2
+ Created on Jun 25, 2013
3
+
4
+ @author: mzwier
5
+ '''
6
+
7
+ import numpy
8
+ from fasthist import histnd
9
+
10
+
11
+ def _test_double(npts=1024 * 1024, loops=3):
12
+ from time import time
13
+
14
+ mine_times = [None] * loops
15
+ theirs_times = [None] * loops
16
+
17
+ # though 1.0 should be sufficient, weirdness in the boundary conditions
18
+ # for numpy.digitize appears to introduce a discrepancy, so throw something
19
+ # greater than 1.0 in for good measure
20
+ binbounds = [[0, 0.5, 1, 1.1] for x in range(3)]
21
+ weights = numpy.random.rand(npts)
22
+ # weights = numpy.ones((npts,), dtype=numpy.float64)
23
+ for n in range(loops):
24
+ testdat = numpy.random.rand(npts, 3)
25
+ mstart = time()
26
+ mine = histnd(testdat, binbounds, weights=weights)
27
+ mstop = time()
28
+ tstart = time()
29
+ theirs = numpy.histogramdd(testdat, binbounds, weights=weights)[0]
30
+ tstop = time()
31
+ mine_times[n] = mstop - mstart
32
+ theirs_times[n] = tstop - tstart
33
+ print(mine)
34
+ print(theirs)
35
+ errsum = numpy.abs(mine - theirs).sum()
36
+ errsum_per_item = errsum / npts
37
+ rel_err = errsum / numpy.abs(weights).sum()
38
+ print('sum of the absolute errors: {} ({} relative, {} per entry)'.format(errsum, rel_err, errsum_per_item))
39
+
40
+ print('mine, best of {}: {}'.format(loops, min(mine_times)))
41
+ print('theirs, best of {}: {}'.format(loops, min(theirs_times)))
42
+
43
+
44
+ def _test_float(npts=1024 * 1024, ndim=3, loops=3):
45
+ from time import time
46
+
47
+ mine_times = [None] * loops
48
+ theirs_times = [None] * loops
49
+
50
+ binbounds = [[0, 0.5, 1, 1.1] for x in range(ndim)]
51
+ # weights = numpy.random.rand(npts)
52
+ weights = numpy.ones((npts,), dtype=numpy.float64)
53
+ for n in range(loops):
54
+ testdat = numpy.require(numpy.random.rand(npts, ndim), numpy.float32)
55
+ print(testdat)
56
+ mstart = time()
57
+ mine = histnd(testdat, binbounds, weights=weights)
58
+ mstop = time()
59
+ tstart = time()
60
+ theirs = numpy.histogramdd(testdat, binbounds, weights=weights)[0]
61
+ tstop = time()
62
+ mine_times[n] = mstop - mstart
63
+ theirs_times[n] = tstop - tstart
64
+ print(mine)
65
+ print(theirs)
66
+ errsum = numpy.abs(mine - theirs).sum()
67
+ errsum_per_item = errsum / npts
68
+ rel_err = errsum / numpy.abs(weights).sum()
69
+ print('sum of the absolute errors: {} ({} relative, {} per entry)'.format(errsum, rel_err, errsum_per_item))
70
+
71
+ print('mine, best of {}: {}'.format(loops, min(mine_times)))
72
+ print('theirs, best of {}: {}'.format(loops, min(theirs_times)))
73
+
74
+
75
+ def _test_uint(npts=1024 * 1024, ndim=3, loops=3):
76
+ from time import time
77
+
78
+ mine_times = [None] * loops
79
+ theirs_times = [None] * loops
80
+
81
+ binbounds = [[0, 1, 2, 3, 4] for x in range(ndim)]
82
+ # weights = numpy.random.rand(npts)
83
+ weights = numpy.ones((npts,), dtype=numpy.float64)
84
+ print('binbounds: {}'.format(binbounds))
85
+ print('weights')
86
+ print(weights)
87
+ for n in range(loops):
88
+ testdat = numpy.require(numpy.random.randint(0, 4, size=(npts, ndim)), numpy.uint16)
89
+ print('test data')
90
+ print(testdat)
91
+ mstart = time()
92
+ mine = histnd(testdat, binbounds, weights=weights)
93
+ mstop = time()
94
+ tstart = time()
95
+ theirs = numpy.histogramdd(testdat, binbounds, weights=weights)[0]
96
+ tstop = time()
97
+ mine_times[n] = mstop - mstart
98
+ theirs_times[n] = tstop - tstart
99
+ print(mine)
100
+ print(theirs)
101
+ errsum = numpy.abs(mine - theirs).sum()
102
+ errsum_per_item = errsum / npts
103
+ rel_err = errsum / numpy.abs(weights).sum()
104
+ print('sum of the absolute errors: {} ({} relative, {} per entry)'.format(errsum, rel_err, errsum_per_item))
105
+
106
+ print('mine, best of {}: {}'.format(loops, min(mine_times)))
107
+ print('theirs, best of {}: {}'.format(loops, min(theirs_times)))
108
+
109
+
110
+ _test_float(npts=1024 * 1024, ndim=1)