MasterMSM 2.0.1__py3-none-any.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.
@@ -0,0 +1,459 @@
1
+ """
2
+ This file is part of the MasterMSM package.
3
+
4
+ """
5
+ #import h5py
6
+ import copy
7
+ import sys
8
+ import math
9
+ import numpy as np
10
+ import mdtraj as md
11
+ import matplotlib.pyplot as plt
12
+
13
+ def load_mdtraj(top=None, xtc=None, stride=None):
14
+ """ Loads trajectories using mdtraj.
15
+
16
+ Parameters
17
+ ----------
18
+ top: str
19
+ The topology file, may be a PDB or GRO file.
20
+ xtc : str
21
+ The trajectory filename.
22
+
23
+ Returns
24
+ -------
25
+ mdtrajs : list
26
+ A list of mdtraj Trajectory objects.
27
+
28
+ """
29
+ return md.load(xtc, top=top, stride=stride)
30
+
31
+ def compute_rama(traj, shift=False, sincos=False):
32
+ """ Computes Ramachandran angles
33
+
34
+ Parameters
35
+ ----------
36
+ traj : md.trajectory
37
+ An MDtraj trajectory object
38
+ shift : bool
39
+ Whether we want to shift the torsions or not
40
+ sincos : bool
41
+ Whether we are calculating the sines and cosines
42
+
43
+ Returns
44
+ -------
45
+ phi : array
46
+ An array with phi torsions
47
+ psi : array
48
+ An array with psi torsions
49
+
50
+ """
51
+ _, phi = md.compute_phi(traj.mdt)
52
+ _, psi = md.compute_psi(traj.mdt)
53
+ if shift:
54
+ return _shift(phi, psi)
55
+ elif sincos:
56
+ return np.column_stack([np.sin(phi), np.cos(phi)]), \
57
+ np.column_stack([np.sin(psi), np.cos(psi)])
58
+ else:
59
+ return phi, psi
60
+
61
+ def compute_contacts(traj, scheme=None, log=False):
62
+ """ Computes inter-residue contacts
63
+
64
+ Parameters
65
+ ----------
66
+ traj : md.trajectory
67
+ An MDtraj trajectory object
68
+ log : bool
69
+ Whether distances are in log scale
70
+
71
+ """
72
+ distances, pairs = md.compute_contacts(traj, scheme=scheme)
73
+ if not log:
74
+ return distances
75
+ else:
76
+ return np.log(distances)
77
+
78
+ def _filter_states(states):
79
+ """
80
+ Fills not-assigned frames (negative labels) when using dbscan or
81
+ hdbscan. Each unassigned frame inherits the label of the previous
82
+ assigned frame; unassigned frames at the start of the trajectory,
83
+ which have no previous frame, inherit the first assigned label.
84
+ The output always has the same length as the input.
85
+
86
+ Raises
87
+ ------
88
+ ValueError
89
+ If no frame is assigned.
90
+
91
+ """
92
+ states = list(states)
93
+ first = next((s for s in states if s >= 0), None)
94
+ if first is None:
95
+ raise ValueError("no frame was assigned to any cluster")
96
+ fs = []
97
+ prev = first
98
+ for s in states:
99
+ if s >= 0:
100
+ prev = s
101
+ fs.append(prev)
102
+ return fs
103
+
104
+ def _shift(phi, psi):
105
+ phi_s, psi_s = copy.deepcopy(phi), copy.deepcopy(psi)
106
+ for i in range(len(phi_s)):
107
+ phi_s[i] = [x + 2*np.pi if x < 0 else x for x in phi_s[i]]
108
+ for i in range(len(psi_s)):
109
+ psi_s[i] = [x + 2*np.pi if (x <-2) else x for x in psi_s[i]]
110
+ return phi_s, psi_s
111
+
112
+ def _inrange(x, lo, hi):
113
+ return 1 if (x > lo and x < hi) else 0
114
+
115
+ def _inbounds(bounds, phi, psi):
116
+ for i in range(0, len(bounds), 4):
117
+ if _inrange(phi, bounds[i], bounds[i+1]) and _inrange(psi, bounds[i+2], bounds[i+3]):
118
+ return 1
119
+ return 0
120
+
121
+ def _state(phi, psi, bounds):
122
+ """ Assign phi, psi (in degrees) to a coarse state.
123
+
124
+ Parameters
125
+ ----------
126
+ phi : float
127
+ Phi dihedral angle in degrees.
128
+ psi : float
129
+ Psi dihedral angle in degrees.
130
+ bounds : dict
131
+ Dict mapping state labels to [phi_min, phi_max, psi_min, psi_max, ...] bounds.
132
+
133
+ Returns
134
+ -------
135
+ k : str
136
+ Assigned state label, or 'O' if unassigned.
137
+
138
+ """
139
+ for k, b in bounds.items():
140
+ if _inbounds(b, phi, psi):
141
+ return k, []
142
+ return 'O', [(phi, psi)]
143
+
144
+ _DEFAULT_BOUNDS = {'A': [-130., -40., -50., 20.],
145
+ 'E': [-180., -40., 125., 180.],
146
+ 'L': [30., 100., -40., 70.]}
147
+
148
+ def default_bounds(states=None):
149
+ """ Default Buchete-Hummer bounds for the requested states.
150
+
151
+ Parameters
152
+ ----------
153
+ states : list, optional
154
+ State labels, from 'A', 'E' and 'L'. Default is all three.
155
+
156
+ Returns
157
+ -------
158
+ bounds : dict
159
+ Maps each label to [phi_min, phi_max, psi_min, psi_max]
160
+ in degrees.
161
+
162
+ """
163
+ if states is None:
164
+ states = ['A', 'E', 'L']
165
+ unknown = [s for s in states if s not in _DEFAULT_BOUNDS]
166
+ if unknown:
167
+ raise ValueError("no default bounds for states %s"%unknown)
168
+ return {s: list(_DEFAULT_BOUNDS[s]) for s in states}
169
+
170
+ def _validate_bounds(b):
171
+ """ Check one state's bounds list, returning it as floats. """
172
+ b = [float(x) for x in b]
173
+ if len(b) == 0 or len(b) % 4 != 0:
174
+ raise ValueError("bounds need groups of four values "\
175
+ "(phi_min, phi_max, psi_min, psi_max), got %i"%len(b))
176
+ for i in range(0, len(b), 4):
177
+ for lo, hi in [(b[i], b[i+1]), (b[i+2], b[i+3])]:
178
+ if not lo < hi:
179
+ raise ValueError("lower bound %g is not below "\
180
+ "upper bound %g"%(lo, hi))
181
+ if lo < -180. or hi > 180.:
182
+ raise ValueError("bounds must lie within "\
183
+ "[-180, 180] degrees")
184
+ return b
185
+
186
+ def _overlaps(bounds):
187
+ """ Pairs of states whose rectangles overlap. """
188
+ rects = [(k, b[i:i+4]) for k, b in bounds.items() \
189
+ for i in range(0, len(b), 4)]
190
+ pairs = []
191
+ for n, (k1, r1) in enumerate(rects):
192
+ for k2, r2 in rects[n+1:]:
193
+ if k1 == k2:
194
+ continue
195
+ if r1[0] < r2[1] and r2[0] < r1[1] and \
196
+ r1[2] < r2[3] and r2[2] < r1[3]:
197
+ if (k1, k2) not in pairs:
198
+ pairs.append((k1, k2))
199
+ return pairs
200
+
201
+ def rama_populations(phi, psi, bounds):
202
+ """ Fraction of (phi, psi) points falling in each state.
203
+
204
+ Counts direct assignments only, before unassigned points
205
+ inherit the previous state. Overlapping regions go to the
206
+ first state in ``bounds``, as in ``discrete_rama``.
207
+
208
+ Parameters
209
+ ----------
210
+ phi, psi : array
211
+ Angles in radians, any shape (all residues are pooled).
212
+ bounds : dict
213
+ State bounds in degrees.
214
+
215
+ Returns
216
+ -------
217
+ pops : dict
218
+ Fraction per state, plus 'O' for unassigned points.
219
+
220
+ """
221
+ f = np.degrees(np.ravel(phi))
222
+ y = np.degrees(np.ravel(psi))
223
+ free = np.ones(len(f), dtype=bool)
224
+ pops = {}
225
+ for k, b in bounds.items():
226
+ inside = np.zeros(len(f), dtype=bool)
227
+ for i in range(0, len(b), 4):
228
+ inside |= (f > b[i]) & (f < b[i+1]) & \
229
+ (y > b[i+2]) & (y < b[i+3])
230
+ inside &= free
231
+ pops[k] = inside.mean() if len(f) else 0.
232
+ free &= ~inside
233
+ pops['O'] = free.mean() if len(f) else 0.
234
+ return pops
235
+
236
+ def plot_rama_bounds(phi, psi, bounds, ax=None, bins=90):
237
+ """ Ramachandran histogram with the state bounds drawn on top.
238
+
239
+ Parameters
240
+ ----------
241
+ phi, psi : array
242
+ Angles in radians, any shape (all residues are pooled).
243
+ bounds : dict
244
+ State bounds in degrees.
245
+ ax : matplotlib Axes, optional
246
+ Axes to draw on. A new figure is made by default.
247
+ bins : int
248
+ Number of histogram bins per axis.
249
+
250
+ Returns
251
+ -------
252
+ ax : matplotlib Axes
253
+
254
+ """
255
+ from matplotlib.colors import LogNorm
256
+ from matplotlib.patches import Rectangle
257
+ if ax is None:
258
+ _, ax = plt.subplots(figsize=(5, 4.5))
259
+ f = np.degrees(np.ravel(phi))
260
+ y = np.degrees(np.ravel(psi))
261
+ h, xe, ye = np.histogram2d(f, y, bins=bins, \
262
+ range=[[-180, 180], [-180, 180]], density=True)
263
+ h = np.ma.masked_where(h == 0, h)
264
+ ax.pcolormesh(xe, ye, h.T, norm=LogNorm(), cmap='Greys')
265
+ pops = rama_populations(phi, psi, bounds)
266
+ colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
267
+ for n, (k, b) in enumerate(bounds.items()):
268
+ c = colors[n % len(colors)]
269
+ for i in range(0, len(b), 4):
270
+ ax.add_patch(Rectangle((b[i], b[i+2]), b[i+1] - b[i], \
271
+ b[i+3] - b[i+2], fill=False, ec=c, lw=2, \
272
+ label="%s (%.1f%%)"%(k, 100*pops[k]) \
273
+ if i == 0 else None))
274
+ ax.set_xlim(-180, 180)
275
+ ax.set_ylim(-180, 180)
276
+ ax.set_xticks(range(-180, 181, 90))
277
+ ax.set_yticks(range(-180, 181, 90))
278
+ ax.set_xlabel(r'$\phi$ (deg)')
279
+ ax.set_ylabel(r'$\psi$ (deg)')
280
+ ax.set_title('unassigned: %.1f%%'%(100*pops['O']))
281
+ ax.legend(loc='upper right', fontsize='small')
282
+ ax.figure.tight_layout()
283
+ return ax
284
+
285
+ def _interactive():
286
+ """ Whether a user can answer input() prompts. """
287
+ try:
288
+ from IPython import get_ipython
289
+ ip = get_ipython()
290
+ if ip is not None and getattr(ip, 'kernel', None) is not None:
291
+ return True
292
+ except ImportError:
293
+ pass
294
+ return sys.stdin is not None and sys.stdin.isatty()
295
+
296
+ def _ask(prompt, input_fn):
297
+ """ Prompt the user; None if nobody can answer. """
298
+ try:
299
+ return input_fn(prompt).strip()
300
+ except (EOFError, OSError, NotImplementedError):
301
+ # pytest capture, closed stdin, or a kernel without stdin
302
+ # (e.g. notebooks executed by nbconvert)
303
+ return None
304
+
305
+ def confirm_bounds(phi, psi, bounds, input_fn=None, plot=True):
306
+ """ Show the Ramachandran bounds and let the user revise them.
307
+
308
+ Draws the pooled Ramachandran histogram with each state's
309
+ region, reports the populations, and asks whether to accept.
310
+ On refusal, asks for new bounds state by state (blank keeps
311
+ the current ones) and shows the result again. When no user
312
+ can answer (scripts without a terminal, pytest, nbconvert),
313
+ the bounds are accepted with a warning.
314
+
315
+ Parameters
316
+ ----------
317
+ phi, psi : array
318
+ Angles in radians, any shape (all residues are pooled).
319
+ bounds : dict
320
+ Proposed state bounds in degrees.
321
+ input_fn : callable, optional
322
+ Replacement for ``input`` (used for testing). When given,
323
+ the session is assumed to be interactive.
324
+ plot : bool
325
+ Whether to draw the histogram (the populations are always
326
+ printed).
327
+
328
+ Returns
329
+ -------
330
+ bounds : dict
331
+ The accepted bounds.
332
+
333
+ """
334
+ import warnings
335
+ bounds = {k: _validate_bounds(b) for k, b in bounds.items()}
336
+ if input_fn is None:
337
+ input_fn = input
338
+ interactive = _interactive()
339
+ else:
340
+ interactive = True
341
+
342
+ while True:
343
+ if plot:
344
+ plot_rama_bounds(phi, psi, bounds)
345
+ plt.show()
346
+ plt.close()
347
+ pops = rama_populations(phi, psi, bounds)
348
+ for k, b in bounds.items():
349
+ print(" %s: %s (%.1f%%)"%(k, b, 100*pops[k]))
350
+ print(" unassigned: %.1f%%"%(100*pops['O']))
351
+ for k1, k2 in _overlaps(bounds):
352
+ print(" warning: %s and %s overlap; points in both go "\
353
+ "to %s"%(k1, k2, k1))
354
+
355
+ ans = _ask("Accept these bounds? [Y/n]: ", input_fn) \
356
+ if interactive else None
357
+ if ans is None:
358
+ warnings.warn("no interactive input available; using "\
359
+ "Ramachandran bounds without confirmation: %s"\
360
+ %bounds, stacklevel=2)
361
+ return bounds
362
+ if ans.lower() in ('', 'y', 'yes'):
363
+ return bounds
364
+ if ans.lower() not in ('n', 'no'):
365
+ print("Please answer y or n.")
366
+ continue
367
+
368
+ for k in bounds:
369
+ while True:
370
+ new = _ask("%s %s -> phi_min, phi_max, psi_min, "\
371
+ "psi_max [, more groups of 4] (blank keeps): "\
372
+ %(k, bounds[k]), input_fn)
373
+ if not new:
374
+ break
375
+ try:
376
+ bounds[k] = _validate_bounds( \
377
+ new.replace(',', ' ').split())
378
+ break
379
+ except ValueError as e:
380
+ print(" invalid: %s"%e)
381
+
382
+ def discrete_rama(phi, psi, bounds=None, states=None,
383
+ return_unassigned=False):
384
+ """ Assign Ramachandran angles to coarse states.
385
+
386
+ Follows the Buchete-Hummer transition-based assignment: frames
387
+ outside every core inherit the state of the previous frame.
388
+ Leading frames, recorded before every dihedral has visited a core,
389
+ have no previous state and are dropped.
390
+
391
+ Parameters
392
+ ----------
393
+ phi : array
394
+ Phi angles, shape (n_frames, n_dihedrals), in radians.
395
+ psi : array
396
+ Psi angles, shape (n_frames, n_dihedrals), in radians.
397
+ bounds : dict, optional
398
+ Custom bounds dict. Default uses standard A/E/L regions.
399
+ states : list, optional
400
+ State labels to include. Defaults to all defined labels.
401
+ return_unassigned : bool, optional
402
+ Also return the fraction of frames outside every core, per
403
+ dihedral, and the number of leading frames dropped.
404
+
405
+ Returns
406
+ -------
407
+ cstates : list
408
+ Per-frame state string (one character per dihedral pair), with
409
+ the leading unassigned frames removed.
410
+ unassigned : array, optional
411
+ Fraction of frames (over the full input) outside every core,
412
+ one value per dihedral. Only if return_unassigned is True.
413
+ ndrop : int, optional
414
+ Number of leading frames dropped. Only if return_unassigned
415
+ is True.
416
+
417
+ Raises
418
+ ------
419
+ ValueError
420
+ If some dihedral never visits any core.
421
+
422
+ References
423
+ ----------
424
+ N.-V. Buchete and G. Hummer, J. Phys. Chem. B (2008).
425
+
426
+ """
427
+ if bounds is None:
428
+ bounds = default_bounds(states)
429
+
430
+ ndih = phi.shape[1]
431
+ if psi.shape[1] != ndih:
432
+ raise ValueError("phi and psi have different numbers of dihedrals")
433
+
434
+ raw = [[_state(f[n] * 180. / math.pi, y[n] * 180. / math.pi,
435
+ bounds)[0] for n in range(ndih)]
436
+ for f, y in zip(phi, psi)]
437
+ raw = np.array(raw, dtype='<U1').reshape(len(raw), ndih)
438
+ unassigned = np.mean(raw == 'O', axis=0)
439
+
440
+ # first frame at which each dihedral is in a core
441
+ assigned = raw != 'O'
442
+ if not np.all(assigned.any(axis=0)):
443
+ never = np.where(~assigned.any(axis=0))[0].tolist()
444
+ raise ValueError("dihedral(s) %s never visit any core" % never)
445
+ ndrop = int(np.max(np.argmax(assigned, axis=0)))
446
+
447
+ # inherit over the full history, then drop the leading frames
448
+ cstates = []
449
+ prev_s_string = ['O'] * ndih
450
+ for row in raw:
451
+ s_string = [prev_s_string[n] if s == 'O' else s
452
+ for n, s in enumerate(row)]
453
+ cstates.append(''.join(s_string))
454
+ prev_s_string = s_string
455
+ cstates = cstates[ndrop:]
456
+
457
+ if return_unassigned:
458
+ return cstates, unassigned, ndrop
459
+ return cstates
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: MasterMSM
3
+ Version: 2.0.1
4
+ Summary: Algorithms to construct master equation / Markov state models
5
+ Author-email: David De Sancho <daviddesancho@gmail.com>
6
+ License-Expression: GPL-2.0-or-later
7
+ Project-URL: Homepage, https://github.com/BioKT/MasterMSM
8
+ Project-URL: Documentation, https://mastermsm.readthedocs.io
9
+ Project-URL: Changelog, https://github.com/BioKT/MasterMSM/blob/master/CHANGELOG.md
10
+ Keywords: markov state model,master equation,molecular dynamics
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
20
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: numpy
25
+ Requires-Dist: scipy
26
+ Requires-Dist: matplotlib
27
+ Requires-Dist: networkx
28
+ Requires-Dist: mdtraj
29
+ Requires-Dist: scikit-learn
30
+ Provides-Extra: hdbscan
31
+ Requires-Dist: hdbscan; extra == "hdbscan"
32
+ Provides-Extra: test
33
+ Requires-Dist: pytest; extra == "test"
34
+ Requires-Dist: ipython; extra == "test"
35
+ Dynamic: license-file
36
+
37
+ [![CI](https://github.com/BioKT/MasterMSM/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/BioKT/MasterMSM/actions/workflows/ci.yml)
38
+ [![codecov](https://codecov.io/gh/BioKT/MasterMSM/branch/master/graph/badge.svg)](https://codecov.io/gh/BioKT/MasterMSM)
39
+ [![Documentation Status](https://readthedocs.org/projects/mastermsm/badge/?version=latest)](https://mastermsm.readthedocs.io/en/latest/?badge=latest)
40
+
41
+ MasterMSM
42
+ =========
43
+ MasterMSM is a Python package for building Markov state models (MSMs)
44
+ from molecular dynamics trajectories, using a formulation based on the
45
+ chemical master equation. Besides the usual transition matrix, it
46
+ estimates the rate matrix of the model, which gives direct access to
47
+ rate coefficients, committors and reactive fluxes.
48
+
49
+ Full documentation is at [mastermsm.readthedocs.io](https://mastermsm.readthedocs.io).
50
+
51
+ Features
52
+ --------
53
+ * **Trajectory handling**: read MD trajectories through
54
+ [MDTraj](https://mdtraj.org) or discrete state trajectories from
55
+ file.
56
+ * **Featurization and dimensionality reduction**: backbone torsions
57
+ and contacts; PCA and TICA.
58
+ * **Discretization**: assignment to Ramachandran basins with
59
+ transition-based assignment (suited to small peptides), k-means
60
+ and HDBSCAN clustering. Different protocols can be run on the same
61
+ data and compared.
62
+ * **Model estimation**: count and transition matrices at any lag
63
+ time; rate matrices from a Taylor expansion or from the maximum
64
+ likelihood propagator-based (MLPB) method; bootstrap errors for
65
+ relaxation times and equilibrium populations.
66
+ * **Validation**: convergence of relaxation times with lag time,
67
+ Chapman-Kolmogorov test, autocorrelation functions of the
68
+ eigenmodes.
69
+ * **Kinetics**: committors, reactive fluxes and rates between sets of
70
+ states; sensitivity analysis of the network.
71
+ * **Coarse-graining**: few-state models from the eigenvectors of the
72
+ MSM.
73
+
74
+ Installation
75
+ ------------
76
+ MasterMSM requires Python >= 3.10 and is installed from
77
+ [PyPI](https://pypi.org/project/MasterMSM/) with `pip`:
78
+
79
+ pip install MasterMSM
80
+
81
+ To work on the code, install it from source instead:
82
+
83
+ git clone https://github.com/BioKT/MasterMSM
84
+ cd MasterMSM
85
+ pip install -e .
86
+
87
+ The dependencies are installed automatically by `pip`: numpy, scipy,
88
+ matplotlib, networkx, [mdtraj](https://mdtraj.org) and scikit-learn.
89
+ If you use conda, you may prefer to install them from conda-forge
90
+ first:
91
+
92
+ conda install -c conda-forge numpy scipy matplotlib \
93
+ networkx mdtraj scikit-learn
94
+
95
+ HDBSCAN clustering additionally needs the optional
96
+ [hdbscan](https://github.com/scikit-learn-contrib/hdbscan) package
97
+ (`pip install "MasterMSM[hdbscan]"`, or `pip install hdbscan`).
98
+
99
+ Quick start
100
+ -----------
101
+ The script below builds an MSM for the alanine dipeptide from a short
102
+ MD trajectory (downloaded from [OSF](https://osf.io/a2vc7) on first
103
+ use).
104
+
105
+ ```python
106
+ from mastermsm.trajectory import traj
107
+ from mastermsm.msm import msm
108
+ from mastermsm.fewsm import fewsm
109
+ from mastermsm.test import download_data as dd
110
+
111
+ if __name__ == "__main__":
112
+ # 1. Load the trajectory and assign frames to Ramachandran states
113
+ dd.download_osf_alaTB()
114
+ tr = traj.TimeSeries(top=dd._DATA_DIR + "/alaTB.gro",
115
+ xtc=dd._DATA_DIR + "/alaTB.xtc")
116
+ tr.discretize(states=["A", "E", "L"])
117
+ tr.find_keys()
118
+
119
+ # 2. Build the MSM at a lag time of 10 ps (units of the trajectory)
120
+ smsm = msm.SuperMSM([tr])
121
+ lagt = 10
122
+ smsm.do_msm(lagt)
123
+ model = smsm.msms[lagt]
124
+ model.do_trans() # transition matrix and its eigenvalues
125
+ model.boots() # bootstrap errors
126
+ print(model.keep_keys) # states in the model: ['A', 'E', 'L']
127
+ print(model.tauT) # relaxation times, ~794 and ~55 ps
128
+ print(model.tau_std)
129
+ print(model.peqT) # equilibrium populations
130
+
131
+ # 3. Rate matrix, committors and flux between E and A
132
+ model.do_rate() # method='Taylor' (default) or 'MLPB'
133
+ print(model.rate)
134
+ model.do_pfold(FF=["A"], UU=["E"])
135
+ print(model.pfold, model.kf)
136
+
137
+ # 4. Coarse-grain into a two-state model
138
+ two_state = fewsm.FEWSM(parent=model, N=2)
139
+ print(two_state.macros)
140
+
141
+ # 5. Validate: relaxation times as a function of lag time
142
+ smsm.convergence_test(time=[5, 10, 20, 50, 100], error=True)
143
+ for lt in sorted(smsm.msms):
144
+ print(lt, smsm.msms[lt].tauT)
145
+ ```
146
+
147
+ MasterMSM uses `multiprocessing`, so scripts need the
148
+ `if __name__ == "__main__":` guard shown above. This is not required
149
+ in a Jupyter notebook.
150
+
151
+ Examples
152
+ --------
153
+ The [examples](examples) folder contains Jupyter notebooks:
154
+
155
+ | Folder | Content |
156
+ |--------|---------|
157
+ | [alanine_dipeptide](examples/alanine_dipeptide) | Complete workflow on the simplest peptide model: discretization, MSM, validation, rate matrix (Taylor and MLPB) |
158
+ | [alanine_pentapeptide](examples/alanine_pentapeptide) | Featurization, TICA and comparison of discretization protocols |
159
+ | [bistable_potential](examples/bistable_potential) | Brownian dynamics on one and two dimensional two-state potentials |
160
+ | [schutte_potential](examples/schutte_potential) | Brownian dynamics on the one dimensional Schütte potential |
161
+
162
+ Most of the data are hosted at [OSF](https://osf.io/a2vc7); each
163
+ notebook says where its data come from. The Brownian dynamics data
164
+ for the bistable and Schütte potentials can be fetched with
165
+
166
+ python examples/download_data.py
167
+
168
+ Testing
169
+ -------
170
+ The tests use `pytest` and download a small amount of MD data on the
171
+ first run:
172
+
173
+ pip install pytest
174
+ pytest --pyargs mastermsm
175
+
176
+ Contributing
177
+ ------------
178
+ Development takes place on the `develop` branch; `master` holds the
179
+ releases. Bug reports and pull requests (against `develop`) are welcome
180
+ on [GitHub](https://github.com/BioKT/MasterMSM/issues).
181
+
182
+ Citation
183
+ --------
184
+ If you use MasterMSM in your work, please cite:
185
+
186
+ @article{mastermsm,
187
+ author = "David De Sancho and Anne Aguirre",
188
+ title = "{MasterMSM: A Package for Constructing Master Equation Models of Molecular Dynamics}",
189
+ year = "2019",
190
+ month = "6",
191
+ journal = "J. Chem. Inf. Model.",
192
+ url = "https://doi.org/10.1021/acs.jcim.9b00468",
193
+ doi = "10.1021/acs.jcim.9b00468"
194
+ }
195
+
196
+ Contributors
197
+ ------------
198
+ * David De Sancho
199
+ * Anne Aguirre
200
+ * Ion Mitxelena
201
+
202
+ License
203
+ -------
204
+ MasterMSM is distributed under the GNU General Public License,
205
+ version 2. See [LICENSE](LICENSE).
@@ -0,0 +1,20 @@
1
+ mastermsm/__init__.py,sha256=wAxkK8w13vqoF47A8iqWdSlIgRRXmZiQ0R4wePZfzhs,22
2
+ mastermsm/fewsm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ mastermsm/fewsm/fewsm.py,sha256=zdO45ccn4RpYIhbJ_uBRH-OdulskoJZU6wylb8qKcoI,9463
4
+ mastermsm/fewsm/fewsm_lib.py,sha256=vB7wzAG7T8aSODEhiJxNobp6OJ1tn_NLn9Wwvz7ELy8,3091
5
+ mastermsm/msm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ mastermsm/msm/msm.py,sha256=fUHUeFV5ZWbDYm03P3ZZLo2SndXpU11LUkV4BR4xLhs,38007
7
+ mastermsm/msm/msm_lib.py,sha256=qf5ocQMVLG4J_-Gw8ERnJKjY0JrGOxXSIRwovBR6DvY,36972
8
+ mastermsm/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ mastermsm/test/download_data.py,sha256=Z6gjz4WWeLsf9yAyrSezH36dJYptpsFKPmcYWHbJtO0,2359
10
+ mastermsm/test/test_fewsm.py,sha256=c04T-ox6G18v8mSE4M1HH2WiU1go1-0xpLOeTAlpnP8,3546
11
+ mastermsm/test/test_msm.py,sha256=RiymRaQqQEE59wQ3k9RVEEqp8XruFFqCGl8DV0YhWRs,42292
12
+ mastermsm/test/test_trajectory.py,sha256=oUo-xiwJDxaVNNXNpyeDHGUj6vZU56_L71d8AxFTKSA,42437
13
+ mastermsm/trajectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ mastermsm/trajectory/traj.py,sha256=cZD_eP4jGn-TDNRhpTwiIgD0BaNp_HYNKYn8YM1b81Y,24612
15
+ mastermsm/trajectory/traj_lib.py,sha256=dSoA5Zd1D_i4VjE6lCOIjGmiwBGtIJp6_a-7v0_j5Kg,14539
16
+ mastermsm-2.0.1.dist-info/licenses/LICENSE,sha256=wDzqAntLQORAL6vQhVdzZyfsPVvFStZKtkct5DIZjK0,18047
17
+ mastermsm-2.0.1.dist-info/METADATA,sha256=-Xh4K4gEa3YPpeyrCQSaKKHecb-8f1DBzTiXY4SoIc4,7832
18
+ mastermsm-2.0.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
19
+ mastermsm-2.0.1.dist-info/top_level.txt,sha256=RGqtnrMyaRvEZW8KrTGYFrTBQkmLGi_hg3YW6ZrtsjU,10
20
+ mastermsm-2.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+