diff --git a/devtools/conda-recipe/meta.yaml b/devtools/conda-recipe/meta.yaml index a627369..446e1d5 100644 --- a/devtools/conda-recipe/meta.yaml +++ b/devtools/conda-recipe/meta.yaml @@ -27,10 +27,11 @@ test: requires: - pytest-cov - nbval + - nose >= 1 imports: - molpx commands: - - pytest -vv --pyargs molpx --cov=molpx --cov-report=xml --current-env --nbval-lax + - pytest -vv --pyargs molpx --cov=molpx --cov-report=xml --current-env --nbval-lax --duration=10 - cp coverage.xml /tmp # [linux] about: diff --git a/doc/source/conf.py b/doc/source/conf.py index 3af843c..dc3d7bd 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -49,14 +49,17 @@ def __getattribute__(self, item): 'pyemma.coordinates.data.featurization.distances', 'nglview', 'matplotlib', + 'matplotlib.pyplot', 'matplotlib.widgets', 'matplotlib.figure', 'matplotlib.axes', 'matplotlib.cm', + 'matplotlib.patches', 'matplotlib.colors', 'IPython.display', 'sklearn.mixture', 'scipy.spatial', + 'scipy.spatial.distance', 'six.moves.urllib.request', 'numpy', # fixme: can not mock ipywidgets because of multi-inheritance within molpx @@ -392,7 +395,7 @@ def __getattribute__(self, item): intersphinx_mapping = {'pyemma': ('http://www.emma-project.org/latest/', None), 'matplotlib': ('http://matplotlib.org/', None), 'mdtraj': ('http://mdtraj.org/latest/', None), - #'pylab' : ('http://matplotlib.org/api/', None), + 'pyplot' : ('http://matplotlib.org/api/', None), 'nglview' : ('http://arose.github.io/nglview/latest/', None) } @@ -408,7 +411,7 @@ def __getattribute__(self, item): nbfiles = [ '0.molPX_quick_intro_Ala2.ipynb', '1.molPX_and_PyEMMA_Features.ipynb', - '2.molPX_TICA_BPTI.ipynb', + '2.molPX_TICA_and_MSMs_BPTI.ipynb', '3.molPX_TICA_Ala2.ipynb', '4.molPX_metadynamics_Di-Ala.ipynb' ] diff --git a/doc/source/index_visualize.rst b/doc/source/index_visualize.rst index 3ee71a4..46d848c 100644 --- a/doc/source/index_visualize.rst +++ b/doc/source/index_visualize.rst @@ -20,9 +20,10 @@ The methods offered by this module are: .. autosummary:: molpx.visualize.FES - molpx.visualize.sample molpx.visualize.traj + molpx.visualize.sample molpx.visualize.correlations + molpx.visualize.MSM molpx.visualize.feature .. automodule:: molpx.visualize diff --git a/molpx/_bmutils.py b/molpx/_bmutils.py index fe7a810..bded7c7 100644 --- a/molpx/_bmutils.py +++ b/molpx/_bmutils.py @@ -4,12 +4,16 @@ try: from sklearn.mixture import GaussianMixture as _GMM except ImportError: + # TODO pin sklearn version and get rid of this from sklearn.mixture import GMM as _GMM +from scipy.spatial.distance import pdist as _pdist, squareform as _squareform + # From pyemma's coordinates from pyemma.coordinates import \ source as _source, \ cluster_regspace as _cluster_regspace, \ + cluster_kmeans as _cluster_kmeans, \ save_traj as _save_traj # From coor.data @@ -164,51 +168,182 @@ def re_warp(array_in, lengths): idxi += ll return warped -def regspace_cluster_to_target(data, n_clusters_target, - n_try_max=5, delta=5., - verbose=False): - r""" - Clusters a dataset to a target n_clusters using regspace clustering by iteratively. " - Work best with 1D data +def regspace_cluster_to_target_kmeans(data, n_clusters_target, + k_centers=1000, + k_stride=50, + n_tol = 5, + max_iter=5, + verbose=False): + r""" Wrapper of :obj:`pyemma.coordinates.cluster_regspace` but using n_clusters (instead of dmin) + as a parameter. + + By using a preliminary :obj:`pyemma.coordinates.cluster_kmeans`-run, a dmin value is optimized to return + approximately :obj:`n_clusters` clustercenters. + + Parameters : + ------------ data: ndarray or list thereof - n_clusters_target: int, number of clusters. - n_try_max: int, default is 5. Maximum number of iterations in the heuristic. - delta: float, defalut is 5. Percentage of n_clusters_target to consider converged. - Eg. n_clusters_target=100 and delta = 5 will consider any clustering between 95 and 100 clustercenters as - valid. Note. Note: An off-by-one in n_target_clusters is sometimes unavoidable + Data to be used for clustering + + n_clusters_target: int, + Approximate number of regularly spaced clustercenters wanted + + k_centers : int, default is 1000 + Number of centers of the preliminary kmeans clustering. Should be between 2 and 10 times + larger than n_clusters + + k_stride : int, default is 50 + Stride for the preliminary kmeans clustering. This clustering is supposed to not be the bottleneck - returns: pyemma clustering object + n_tol : int, default is 5 + Consider :obj:`n_clusters_target` \pm :obj:`n_tol` as converged + + max_iter : int, default is 5 + Will stop after :obj:`max_iter` regspace-clustering attempts regardless + + Returns : + -------- + cl : a :obj:`pyemma.coordinates.cluster_regspace` object with approximately :obj:n_clusters tested:True """ - delta = delta/100 - ndim = _np.vstack(data).shape[0] - assert ndim >= n_clusters_target, "Cannot cluster " \ - "%u datapoints on %u clustercenters. Reduce the number of target " \ - "clustercenters."%(_np.vstack(data).shape[0], n_clusters_target) - # Works well for connected, 1D-clustering, - # otherwise it's bad starting guess for dmin - cmax = _np.vstack(data).max() - cmin = _np.vstack(data).min() - dmin = (cmax-cmin)/(n_clusters_target+1) - - err = _np.ceil(n_clusters_target*delta) - cl = _cluster_regspace(data, dmin=dmin, max_centers=5000) - for cc in range(n_try_max): - n_cl_now = cl.n_clusters - delta_cl_now = _np.abs(n_cl_now - n_clusters_target) - if not n_clusters_target-err <= cl.n_clusters <= n_clusters_target+err: - # Cheap (VERY BAD IN HIGH DIM) heuristic to get relatively close relatively quick - dmin = cl.dmin*cl.n_clusters/ n_clusters_target - cl = _cluster_regspace(data, dmin=dmin, max_centers=5000)# max_centers is given so that we never reach it (dangerous) - else: + + # Listify if only one array + if isinstance(data, _np.ndarray): + data = [data] + + # The parameter k_centers=1000 is just an initialization and has only impact on speed. + # TODO: consider hard coding it + # We try to catch some pathological data inputs with too little data + n_frames_kmeans = _np.sum([len(idata[::k_stride]) for idata in data]) + if n_frames_kmeans< k_centers: + k_stride = 1 + n_frames_kmeans = _np.sum([len(idata[::k_stride]) for idata in data]) + k_centers = _np.min((n_frames_kmeans, k_centers)) + + # 1. Arrive at an approximate dmin by + # 1.1 Preliminary clustering + pre_cl = _cluster_kmeans(data, k=k_centers, stride=k_stride, init_strategy='uniform' ) + # 1.2 Distance matrix + D = _squareform(_pdist(pre_cl.clustercenters)) + # 1.3 Define the objective function to be optimized for dmin by interval_schachtelung + J = lambda dmin: len(regspace_from_distance_matrix(D, dmin)) + # 1.4 Optimize for dmin on the preliminary clustercenters + dmin = interval_schachtelung(J, [D.min(), D.max()], target=n_clusters_target, verbose=verbose) + # 2. Now that we have an approximate dmin, cluster in regspace iteratively: + cl = _cluster_regspace(data, dmin=dmin) + + ii = 0 + while (_np.abs(cl.n_clusters-n_clusters_target) > n_tol): + if ii >= max_iter: + print("Reached max_iter %u."%ii) break - if verbose: - print('cl iter %u %u -> %u (Delta to target (%u +- %u): %u'%(cc, n_cl_now, cl.n_clusters, - n_clusters_target, err, delta_cl_now)) + # Distance matrix + D = _squareform(_pdist(cl.clustercenters)) + # Define the objective function to be optimized for dmin + J = lambda dmin: len(regspace_from_distance_matrix(D, dmin)) + # Optimize, starting at the actual dmin + dmin = interval_schachtelung(J, [D.min(), D.max()], target=n_clusters_target, verbose=verbose) + cl = _cluster_regspace(data, dmin) + #print(ii, dmin, cl.n_clusters) + ii +=1 + return cl +def interval_schachtelung(f, interval, target=0, eps=1, maxiter=1000, verbose=False): + r""" + Find the value m s.t. f(m) = target +- eps using interval optimization + + :param function to be optimized + :param interval: iterable of len 2 with the left and right limits of input interval + :param target: objective + :param eps: convergence criterion + :param verbose: whether to inform of each iteration or not + :return: m + + tested : True + """ + + left, right = interval[0], interval[1] + def inform(left, fl, right, fr, middle, fm, delta, eps, str0=''): + print(str0) + print('left: %04.2f, f(left) = %04.2f' % (left, fl)) + print('middle: %04.2f, f(middle) = %04.2f' % (middle, fm)) + print('right: %04.2f, f(right) = %04.2f' % (right, fr)) + print('delta, eps = %f %f, conv: %s'%(delta, eps, ~(delta >= eps))) + print() + + middle = (left + right) / 2 + fl, fr, fm = f(left), f(right), f(middle) + delta = _np.abs(fm - target) + if verbose: + inform(left, fl, right, fr, middle, fm, delta, eps, str0='Init:') + + cc = 0 + while delta >= eps and cc= target >= fm: + right, fr = middle, fm + elif fm <= target <= fr or fm >= target >= fr: + left, fl = middle, fm + else: + print(fl, target, fm, middle) + raise Exception("Failed while optimizing") + cc += 1 + return middle + + + +def regspace_from_distance_matrix(D, dmin): + r""" Return the indices idxs of the rows/columns of the symmetric matrix (D[idxs,idxs] > dmin).all() == True + + Can be used as an analogue to :obj:pyemma.coordinates.cluster_regpsace if all pairwise distances have been + recomputed + + + Parameters : + ------------ + + D: 2D np.ndarray of shape (m,m) + Symmetric matrix containing pairwise distances + + dmin : float or int, must be > 0 + Cutoff + + Returns : + --------- + + centers : list + Integers of the the rows/columns that contain distances > dmin + + """ + + assert _np.allclose(D, D.T), "Input matrix is not symmetric" + assert dmin >= 0, ("dmin has to be > 0, not", dmin) + + Dprime = _np.copy(D) + _np.fill_diagonal(Dprime, _np.inf) + assigned = [] + for ii, irow in enumerate(Dprime): + if ii not in assigned: + assigned.extend(_np.argwhere(irow < dmin).flatten()) + assigned = _np.unique(assigned) + centers = [ii for ii in range(Dprime.shape[0]) if ii not in assigned] + Dprime = Dprime[centers, :] + Dprime = Dprime[:, centers] + + # This is the method testing itself + #assert Dprime.min() > dmin, (Dprime.min(), dmin) + + return centers + def min_disp_path(start, path_of_candidates, exclude_coords=None, history_aware=False): r""""Returns a list of indices [i,j,k...] s.t. the distances @@ -592,7 +727,10 @@ def save_traj_wrapper(traj_inp, indexes, outfile, top=None, stride=1, chunksize= Parameters ----------- - traj_inp : :pyemma:`FeatureReader` object or :mdtraj:`Trajectory` object or list of :mdtraj:`Trajectory` objects + traj_inp : Can be of many types + :pyemma:`FeatureReader` object + :mdtraj:`Trajectory` object or list thereof + a list of strings pointing to filenames returns: see the return values of :pyemma:`save_traj` """ @@ -604,11 +742,23 @@ def save_traj_wrapper(traj_inp, indexes, outfile, top=None, stride=1, chunksize= if isinstance(traj_inp, (_FeatureReader, _FragmentedTrajectoryReader)) or _is_string(traj_inp[0]): geom_smpl = _save_traj(traj_inp, indexes, None, top=top, stride=stride, chunksize=chunksize, image_molecules=image_molecules, verbose=verbose) + elif isinstance(traj_inp[0], _md.Trajectory): - file_idx, frame_idx = indexes[0] - geom_smpl = traj_inp[file_idx][frame_idx] - for file_idx, frame_idx in indexes[1:]: - geom_smpl = geom_smpl.join(traj_inp[file_idx][frame_idx]) + top = traj_inp[0].top + n_frames = len(indexes) + xyz = _np.zeros((n_frames, top.n_atoms, 3)) + uc_lengths = _np.zeros((n_frames, 3)) + uc_angles = _np.zeros((n_frames, 3)) + time = _np.zeros(n_frames) + for ii, (file_idx, frame_idx) in enumerate(indexes): + xyz[ii, :, :] = traj_inp[file_idx].xyz[frame_idx*stride].squeeze() + uc_lengths[ii, :] = traj_inp[file_idx].unitcell_lengths[frame_idx*stride] + uc_angles[ii, :] = traj_inp[file_idx].unitcell_angles[frame_idx*stride] + time[ii] = traj_inp[file_idx].time[frame_idx*stride] + + geom_smpl = _md.Trajectory(xyz, top, time=time, + unitcell_lengths=uc_lengths, + unitcell_angles=uc_angles) else: raise TypeError("Cant handle input of type %s now"%(type(traj_inp[0]))) @@ -659,7 +809,7 @@ def get_ascending_coord_idx(pos, fail_if_empty=False, fail_if_more_than_one=Fals idxs = _np.argwhere(_np.all(_np.diff(pos,axis=0)>0, axis=0)).squeeze() if isinstance(idxs, _np.ndarray) and idxs.ndim==0: idxs = idxs[()] - elif idxs == [] and fail_if_empty: + elif len(idxs)==0 and fail_if_empty: raise ValueError('No column was found in ascending order') if _np.size(idxs) > 1: @@ -749,10 +899,6 @@ def smooth_geom(geom, n, geom_data=None, superpose=True, symmetric=True): Note: you might need to re-orient this averaged geometry again """ - # Input checking here, otherwise we're seriously in trouble - - - # Get the indices necessary for the running average frame_idxs, frame_windows = running_avg_idxs(geom.n_frames, n, symmetric=symmetric) @@ -769,6 +915,7 @@ def smooth_geom(geom, n, geom_data=None, superpose=True, symmetric=True): xyz = _np.zeros((len(frame_idxs), geom.n_atoms, 3)) for ii, idx in enumerate(frame_idxs): #print(ii, idx, frame_windows[ii][n]) + # TODO avoid casting an entire geometry (which triggers deepcopys which are time consuming) if superpose: xyz[ii,:,:] = geom[frame_windows[ii]].superpose(geom, frame=frame_windows[ii][n]).xyz.mean(0) else: @@ -899,6 +1046,8 @@ def most_corr(correlation_input, geoms=None, proj_idxs=None, feat_name=None, n_a for ii in proj_idxs: icorr = corr[:, ii] + # NaN's will screw up this argsort, so + icorr[_np.isnan(icorr)] = 0 most_corr_idxs.append(_np.abs(icorr).argsort()[::-1][:n_args]) most_corr_vals.append([icorr[jj] for jj in most_corr_idxs[-1]]) if geoms is not None and avail_FT: @@ -910,33 +1059,63 @@ def most_corr(correlation_input, geoms=None, proj_idxs=None, feat_name=None, n_a most_corr_labels.append([featurizer.describe()[jj] for jj in most_corr_idxs[-1]]) if avail_FT: - if len(featurizer.active_features) > 1: - pass - # TODO write a warning - else: - ifeat = featurizer.active_features[0] - most_corr_atom_idxs.append(atom_idxs_from_feature(ifeat)[most_corr_idxs[-1]]) + most_corr_atom_idxs.append([atom_idxs_from_general_input(featurizer)[ii] for ii in most_corr_idxs[-1]]) for ii, iproj in enumerate(proj_names): info.append({"lines":[], "name":iproj}) for jj, jidx in enumerate(most_corr_idxs[ii]): if avail_FT: - istr = 'Corr[%s|feat] = %2.1f for %-30s (feat nr. %u, atom idxs %s' % \ + istr = 'Corr[%s|feat] = %2.1f || %-30s || feat nr. %u, atom idxs %s' % \ (iproj, most_corr_vals[ii][jj], most_corr_labels[ii][jj], jidx, most_corr_atom_idxs[ii][jj]) else: - istr = 'Corr[%s|feat] = %2.1f (feat nr. %u)' % \ + istr = 'Corr[%s|feat] = %2.1f || nfeat nr. %u' % \ (iproj, most_corr_vals[ii][jj],jidx) info[-1]["lines"].append(istr) - corr_dict = {'idxs': most_corr_idxs, + corr_dict = CorrelationDict({'idxs': most_corr_idxs, 'vals': most_corr_vals, 'labels': most_corr_labels, 'feats': most_corr_feats, 'atom_idxs': most_corr_atom_idxs, - 'info':info} + 'info':info}) return corr_dict +class CorrelationDict(dict): + r""" This is just a dictionary + with the print method + rewritten to a pretty-print""" + def __str__(self): + nfeats = len(self["idxs"]) + output = 'Correlation dictionary for %u projections\n'%nfeats + for ii in range(nfeats): + for iline in self["info"][ii]["lines"]: + output += ' '+iline.replace(' || ','\n ')+'\n' + output+='\n' + + return output + +def atom_idxs_from_general_input(input): + r""" + Provided with anything that has a list of ifet.active_features, return the representative + atom indices for each feature component + :param input: can be TICA, PCA, or MDfeaturizer + :return: list of input.dimension() with the atoms involved in each feature + """ + + if isinstance(input, (_TICA, _PCA)): + MDfeat = input.data_producer.featurizer + elif isinstance(input, _MDFeaturizer): + MDfeat = input + else: + raise TypeError("Sorry, input has to be of type %s, not %s" % ([_MDFeaturizer, _TICA, _PCA], type(input))) + + # Get atom lists for each active feature + out_idxs = [atom_idxs_from_feature(jfeat) for jfeat in MDfeat.active_features] + # Get one single list + return [item for sublist in out_idxs for item in sublist] + + def atom_idxs_from_feature(ifeat): r""" Return the atom_indices that best represent this input feature @@ -944,10 +1123,11 @@ def atom_idxs_from_feature(ifeat): Parameters ---------- - ifeat : input feature, can be of two types: - a :any:`pyemma.coordinates.featurizer` (Distancefeaturizer, AngleFeaturizer etc) or - a :any:`pyemma.coordinates.data.featurization.featurizer.MDFeaturizer` itself, in which case the first of the - obj:`ifeat.active_features` will be used + ifeat : input featurizer: + a PyEMMA Feature. Accepted are + DistanceFeature, AngleFeature, DihedralFeature, ResidueMinDistanceFeature, and + SelectionFeature, + #TODO include link to PyEMMA objects in docstring Returns ------- @@ -955,18 +1135,13 @@ def atom_idxs_from_feature(ifeat): atom_indices : list with the atoms indices representative of this feature, whatever the feature """ - try: - ifeat = ifeat.active_features[0] - except AttributeError: - pass - if isinstance(ifeat, _DF) and not isinstance(ifeat, _ResMinDF): return ifeat.distance_indexes elif isinstance(ifeat, _SF): return _np.repeat(ifeat.indexes, 3) elif isinstance(ifeat, _ResMinDF): # Comprehend all the lists!!!! - return _np.vstack([[list(ifeat.top.residue(pj).atoms_by_name('CA'))[0].index for pj in pair] for pair in ifeat.contacts]) + return _np.vstack([[get_repr_atom_for_residue(ifeat.top.residue(pj)).index for pj in pair] for pair in ifeat.contacts]) if isinstance(ifeat, (_DihF, _AF)): ai = ifeat.angle_indexes if ifeat.cossin: @@ -975,6 +1150,23 @@ def atom_idxs_from_feature(ifeat): else: raise NotImplementedError('bmutils.atom_idxs_from_feature cannot interpret the atoms behind %s yet'%ifeat) +def get_repr_atom_for_residue(rr, cands = ['CA','C','C1']): + r""" + Tries to return a representative atom per residue. For AAs, it is the CA, + then, the next atom-name in cands is looked for + :param rr: mdtraj-residue object + """ + + if rr.n_atoms==1: + return list(rr.atoms)[0] + + for cc in cands: + out = list(rr.atoms_by_name(cc)) + if len(out)>0: + return out[0] + if len(out)==0: + raise ValueError("Could not find any atoms named %s in the residue %s"%(cands, rr)) + def add_atom_idxs_widget(atom_idxs, ngl_wdg, color_list=None, radius=1): r""" provided a list of atom_idxs and a ngl_wdg, try to represent them as well as possible in the ngl_wdg @@ -1019,15 +1211,15 @@ def add_atom_idxs_widget(atom_idxs, ngl_wdg, color_list=None, radius=1): ngl_wdg.add_spacefill(selection=[iidxs], radius=radius, color=color, component=cc) elif _np.ndim(iidxs)>0 and len(iidxs)==2: ngl_wdg.add_distance(atom_pair=[[ii for ii in iidxs]], # yes it has to be this way for now - color=color, - #label_color='black', - label_size=0, + color=color, + label_size=0, component=cc) - # TODO add line thickness as **kwarg + # TODO add line thickness as **kwarg, see nglview usage questions for answer elif _np.ndim(iidxs) > 0 and len(iidxs) in [3,4]: ngl_wdg.add_spacefill(selection=iidxs, radius=radius, color=color, component=cc) else: - print("Cannot represent features involving more than 5 atoms per single feature") + raise NotImplementedError("Cannot represent features involving more than 5 atoms per single feature") + return ngl_wdg diff --git a/molpx/_linkutils.py b/molpx/_linkutils.py index f37a763..1649b76 100644 --- a/molpx/_linkutils.py +++ b/molpx/_linkutils.py @@ -4,12 +4,13 @@ from matplotlib.colors import is_color_like as _is_color_like from matplotlib.axes import Axes as _mplAxes from matplotlib.figure import Figure as _mplFigure +from matplotlib.patches import Rectangle as _Rectangle from IPython.display import display as _ipydisplay from pyemma.util.types import is_int as _is_int from scipy.spatial import cKDTree as _cKDTree -from ._bmutils import get_ascending_coord_idx +from ._bmutils import get_ascending_coord_idx, add_atom_idxs_widget from mdtraj import Trajectory as _mdTrajectory from nglview import NGLWidget as _NGLwdg @@ -46,8 +47,6 @@ def pts_per_axis_unit(mplax, pt_per_inch=72): inch_per_unit = span_inch / span_units return inch_per_unit * pt_per_inch - - def update2Dlines(iline, x, y): """ provide a common interface to update objects on the plot to a new position (x,y) depending @@ -80,7 +79,6 @@ def update2Dlines(iline, x, y): # TODO: FIND OUT WNY EXCEPTIONS ARE NOT BEING RAISED raise TypeError("what is this type of 2Dline?") - class ClickOnAxisListener(object): def __init__(self, ngl_wdg, crosshairs, showclick_objs, ax, pos, list_mpl_objects_to_update): @@ -91,8 +89,10 @@ def __init__(self, ngl_wdg, crosshairs, showclick_objs, ax, pos, self.pos = pos self.list_mpl_objects_to_update = list_mpl_objects_to_update self.list_of_dots = [None]*self.pos.shape[0] + self.list_of_rects = [None] * self.pos.shape[0] self.fig_size = self.ax.figure.get_size_inches() self.kdtree = None + self.axlims = _np.hstack((self.ax.get_xlim(), self.ax.get_ylim())) def build_tree(self): # Use ax.transData to compute distance in pixels @@ -104,6 +104,19 @@ def build_tree(self): def figure_changed_size(self): return not _np.allclose(self.fig_size, self.ax.figure.get_size_inches()) + @property + def axes_changed(self): + current_axlims = _np.hstack((self.ax.get_xlim(), self.ax.get_ylim())) + return not _np.allclose(current_axlims, self.axlims) + + def remove_last_contacts(self): + try: + self.ngl_wdg._CtcsInWid[self.ngl_wdg._CtcsLast].hide() + self.list_of_rects[self.ngl_wdg._CtcsLast].remove() + self.list_of_rects[self.ngl_wdg._CtcsLast] = None + except AttributeError: + pass + def __call__(self, event): # Wait for the first click or a a figsize change # to build the kdtree @@ -111,6 +124,16 @@ def __call__(self, event): self.build_tree() self.fig_size = self.ax.figure.get_size_inches() + # Check axes changes (e.g. in zooming) + if self.axes_changed: + self.build_tree() # rebuild tree + # store new limits + self.axlims = _np.hstack((self.ax.get_xlim(), + self.ax.get_ylim())) + + # Remove spurious contacts from the zooming-click + self.remove_last_contacts() + # Was the click inside the bounding box? if self.ax.get_window_extent().contains(event.x, event.y): if self.crosshairs: @@ -122,6 +145,8 @@ def __call__(self, event): update2Dlines(idot, self.pos[index, 0], self.pos[index, 1]) self.ngl_wdg.isClick = True + + # The sticky cases like _CtcsInWid or _GeomsInWid are update here instead of via the mplobjects to update if hasattr(self.ngl_wdg, '_GeomsInWid'): # We're in a sticky situation if event.button == 1: @@ -138,6 +163,29 @@ def __call__(self, event): if not self.ngl_wdg._GeomsInWid[index].is_visible() and self.list_of_dots[index] is not None: self.list_of_dots[index].remove() self.list_of_dots[index] = None + + + elif hasattr(self.ngl_wdg, '_CtcsInWid'): + if event.button == 1: + self.ngl_wdg._CtcsInWid[index].show() + # Plot and store the rectangle in case there wasn't + if self.list_of_rects[index] is None: + rectangle_padding = 0 # TODO future plans to make rectangles catch more than one ctc + x, y = self.pos[index] + self.list_of_rects[index] = self.ax.add_patch(_Rectangle( + (x - .5 - rectangle_padding, + y - .5 - rectangle_padding), # (x,y) + 1 + 2 * rectangle_padding, # width + 1 + 2 * rectangle_padding, # height, + fill=False, + linewidth=2, + edgecolor=self.ngl_wdg._CtcsInWid[index].color)) + elif event.button in [2,3]: + # Pressed left + self.ngl_wdg._CtcsInWid[index].hide() + if self.list_of_rects[index] is not None: + self.list_of_rects[index].remove() + self.list_of_rects[index] = None else: # We're not sticky, just go to the frame self.ngl_wdg.frame = index @@ -179,8 +227,8 @@ def append_if_existing(self, args0, startswith_arg="linked_"): self.__dict__[attrname] += iarg.__dict__[attrname] def auto_append_these_mpx_attrs(iobj, *attrs): - r""" The attribute s name is automatically derived - from the attribute s type via a type:name dictionary + r""" The attribute's name is automatically derived + from the attribute's type via a type:name dictionary *attrs : any number of unnamed objects of the types in type2attrname. If the object type is a list, it will be flattened prior to attempting @@ -238,6 +286,80 @@ def __call__(self, change): print("caught index error with index %s (new=%s, old=%s)" % (_idx, change["new"], change["old"])) #print("set xy = (%s, %s)" % (x[_idx], y[_idx])) + if hasattr(self.ngl_wdg, '_MatshowData'): + self.ngl_wdg._MatshowData["image"].set_data(self.ngl_wdg._MatshowData["data"][_idx]) + + +class ContactInNGLWidget(object): + r""" + returns an object that is aware about its own atom-indices and + its own representation index in the widget. It also has access to the widget itself + With that knowlegde, one can use the methods .show() and .hide() + + param : ngl_widget, the widget upon which to superpose the contact + param : atom_indices, len(2), atom indices involved in this contact + param : contact_index, int, the index corresponding to this contact + """ + + + def __init__(self, ngl_wdg, atom_indices, contact_index, + component_to_draw_on=0, + verbose=False, + color=None, + sequential_residues=True): + r""" + + :param ngl_wdg: + :param atom_indices: iterable of len(2) with two integers (zero indexed atom indices) + The pair of atoms that are representative of this contact + When the method .show() of this class is called, a distance representation (=a line joining two atoms) + will be added to the :obj:`ngl_widget`. + :param contact_index: integer + The position of this contact in the contact list + :param component_to_draw_on: + :param verbose: + :param color: + """ + assert len(atom_indices)==2, "ContactInNGLWidget takes a list with two elements as input, not len(%u)"%len(atom_indices) + assert [isinstance(ii, int) for ii in atom_indices], "The atom indices have to be type int" + + self.atom_indices = atom_indices + self.ngl_wdg = ngl_wdg + self.contact_index = contact_index + self.verbose = verbose + self.top = self.ngl_wdg._trajlist[component_to_draw_on].trajectory.top + self.comp = component_to_draw_on + self.shown = False + self.color = color + + def show(self): + if not self.shown: + if self.verbose: + print("Showing contact %s via atoms %s"%(' '.join(['%s'%self.top.atom(ii).residue for ii in self.atom_indices]), + ' '.join(['%s' % self.top.atom(ii) for ii in self.atom_indices]))) + + self.shown = True + add_atom_idxs_widget([self.atom_indices], self.ngl_wdg, color_list=[self.color]) + self.ngl_wdg._CtcsLast = self.contact_index + + def hide(self): + if self.shown: + #print(self.ngl_wdg._ngl_repr_dict[str(self.comp)].keys()) + for key in self.matching_repr_keys: + self.ngl_wdg._remove_representation(self.comp, repr_index=int(key)) + self.shown = False + + @property + def matching_repr_keys(self): + # Given that the _ngl_repr_dict gets updated elsewhere, this is the most robust way of + # finding this contact's representations + try: + res = [key for key, value in self.ngl_wdg._ngl_repr_dict[str(self.comp)].items() if value["type"] == "distance" + and _np.allclose(_np.sort(value["params"]["atomPair"]), _np.sort(self.atom_indices))] + except KeyError: + res = [] + return res + class GeometryInNGLWidget(object): r""" returns an object that is aware of where its geometries are located in the NGLWidget their representation status @@ -400,6 +522,8 @@ def link_ax_w_pos_2_nglwidget(ax, pos, ngl_wdg, # Are we in a sticky situation? if hasattr(ngl_wdg, '_GeomsInWid'): sticky = True + elif hasattr(ngl_wdg, "_CtcsInWid"): + pass else: assert ngl_wdg.trajectory_0.n_frames == pos.shape[0], \ ("Mismatching frame numbers %u vs %u" % (ngl_wdg.trajectory_0.n_frames, pos.shape[0])) @@ -462,6 +586,7 @@ def link_ax_w_pos_2_nglwidget(ax, pos, ngl_wdg, # Connect axes to widget axes_widget = _AxesWidget(ax) if directionality in [None, 'a2w']: + # TODO since zooming events also contain button_release events this will be triggered as well axes_widget.connect_event('button_release_event', CLA_listener) # Connect widget to axes diff --git a/molpx/generate.py b/molpx/generate.py index 0314714..5cefa32 100644 --- a/molpx/generate.py +++ b/molpx/generate.py @@ -117,8 +117,8 @@ def projection_paths(MD_trajectories, MD_top, projected_trajectories, "min_disp": _defdict(dict) } # Cluster in regspace along the dimension you want to advance, to approximately n_points - cl = _bmutils.regspace_cluster_to_target([jdata[:,[coord]] for jdata in idata], - n_points, n_try_max=3, + cl = _bmutils.regspace_cluster_to_target_kmeans([jdata[:,[coord]] for jdata in idata], + n_points, max_iter=3, verbose=verbose, ) @@ -209,7 +209,7 @@ def projection_paths(MD_trajectories, MD_top, projected_trajectories, #TODO : consider storing the data in each dict. It's redundant but makes each dict kinda standalone - return paths_dict, idata + return paths_dict , idata # why were we returning idata in the first place? def sample(MD_trajectories, MD_top, projected_trajectories, @@ -298,7 +298,7 @@ def sample(MD_trajectories, MD_top, projected_trajectories, cl = projected_trajectories except: idata = _bmutils.data_from_input(projected_trajectories) - cl = _bmutils.regspace_cluster_to_target([dd[:,proj_idxs] for dd in idata], n_points, n_try_max=10, verbose=verbose) + cl = _bmutils.regspace_cluster_to_target_kmeans([dd[:,proj_idxs] for dd in idata], n_points, verbose=verbose) pos = cl.clustercenters cat_smpl = cl.sample_indexes_by_cluster(_np.arange(cl.n_clusters), n_geom_samples) diff --git a/molpx/notebooks/0.molPX_quick_intro_Ala2.ipynb b/molpx/notebooks/0.molPX_quick_intro_Ala2.ipynb index bd316f1..bfe1ae5 100644 --- a/molpx/notebooks/0.molPX_quick_intro_Ala2.ipynb +++ b/molpx/notebooks/0.molPX_quick_intro_Ala2.ipynb @@ -16,13 +16,11 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true, "scrolled": true }, "outputs": [], "source": [ - "import molpx\n", - "%matplotlib ipympl" + "import molpx" ] }, { @@ -35,9 +33,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "top = molpx._molpxdir(join='notebooks/data/ala2.pdb')\n", @@ -70,27 +66,34 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "cbfb5bb0aa114fce88e90ae57fdd123f", + "model_id": "617716dc19d74f83aa878ea096b78ea3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25-05-18 14:00:15 pyemma.coordinates.clustering.kmeans.KmeansClustering[0] INFO Cluster centers converged after 5 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d18c35c79bbb43a28b230975961aa179", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -130,27 +133,34 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e5ff550df1784bc698a701c5fcad3490", + "model_id": "564bc9bc5ef841038a73725c9978b7dc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25-05-18 14:00:21 pyemma.coordinates.clustering.kmeans.KmeansClustering[9] INFO Cluster centers converged after 7 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1a8b49758df04e2dbc50606d7fea0b7e", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXVBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXVBox(children=(MolPXHBox(children=(VBox(children=(Button(description='NGL widgets', layout=Layout(width='100%'), style=ButtonStyle()), VBox(children=(NGLWidget(count=3334),), layout=Layout(border='solid'))), layout=Layout(height='2.0in', width='5.0in')), FigureCanvasNbAgg())), MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -158,12 +168,6 @@ } ], "source": [ - "from molpx import visualize, _linkutils\n", - "from imp import reload\n", - "reload(visualize)\n", - "reload(_linkutils)\n", - "from matplotlib import pyplot as plt\n", - "plt.close('all')\n", "mpx_wdg_box = molpx.visualize.traj(MD_trajfiles, \n", " top, \n", " rama_files,\n", @@ -185,9 +189,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -204,27 +206,34 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e427834063234005be0f3c41f8f7d04e", + "model_id": "875dcdec40b246e984881c17785b16f4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25-05-18 14:00:31 pyemma.coordinates.clustering.kmeans.KmeansClustering[18] INFO Cluster centers converged after 6 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "46d4a5d0d9e647cb9838735556fae885", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -252,12 +261,12 @@ "metadata": {}, "source": [ "# The ```mpx_wdg_box```\n", - "It is a class derived from the ipython widgets HBox and VBox, with molPX's extra information as attributes starting with \"linked_*\" " + "It is a class derived from the ipython widgets [HBox](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html?highlight=Hbox#Container/Layout-widgets) and [VBox](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html?highlight=Hbox#Container/Layout-widgets), with molPX's extra information as attributes starting with `linked_*` " ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -297,7 +306,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/molpx/notebooks/1.molPX_and_PyEMMA_Features.ipynb b/molpx/notebooks/1.molPX_and_PyEMMA_Features.ipynb index c2cc5a9..f4a5d00 100644 --- a/molpx/notebooks/1.molPX_and_PyEMMA_Features.ipynb +++ b/molpx/notebooks/1.molPX_and_PyEMMA_Features.ipynb @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -43,8 +43,7 @@ "dt = 244 #saving interval in the .xtc files, in ns\n", "\n", "import molpx\n", - "from matplotlib import pylab as plt\n", - "%matplotlib ipympl\n", + "from matplotlib import pyplot as plt\n", "import pyemma\n", "import numpy as np\n", "\n", @@ -55,10 +54,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "# Create a memory representation of the trajectories\n", @@ -67,47 +64,70 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e07101841c2542439184b1e66b4da372", + "model_id": "feaf7c33ce0b4e7ab136de29a5c70eeb", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" + "A Jupyter Widget" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "113eec10d13c4fac8c620c7d30c7a519", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, { "data": { "text/plain": [ "('DIST: PRO 9 CA 8 - LYS 15 CA 14',\n", " 'DIST: PRO 9 CA 8 - TYR 23 CA 22',\n", - " )" + " MDFeaturizer with features:\n", + " ['DIST: ARG 1 CA 0 - ASP 3 CA 2',\n", + " 'DIST: ARG 1 CA 0 - CYS 5 CA 4',\n", + " 'DIST: ARG 1 CA 0 - GLU 7 CA 6',\n", + " 'DIST: ARG 1 CA 0 - PRO 9 CA 8',\n", + " 'DIST: ARG 1 CA 0 - THR 11 CA 10',\n", + " 'DIST: ARG 1 CA 0 - PRO 13 CA 12',\n", + " 'DIST: ARG 1 CA 0 - LYS 15 CA 14',\n", + " 'DIST: ARG 1 CA 0 - ARG 17 CA 16',\n", + " 'DIST: ARG 1 CA 0 - ILE 19 CA 18',\n", + " 'DIST: ARG 1 CA 0 - TYR 21 CA 20', ...])" ] }, - "execution_count": 3, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -130,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": { "scrolled": false }, @@ -138,56 +158,55 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e585639a2d214150b6bf44bddaf096cd", + "model_id": "bbd5a7072c454ab79f03f3c83c41e3ea", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" + "A Jupyter Widget" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 16:34:21 pyemma.coordinates.clustering.kmeans.KmeansClustering[2] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "de2388685daf4abe9f7bee72bbf84972", + "model_id": "58ef338dfc4d48c4b2c4f6715ca911f8", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=95), FigureCanvasNbAgg()))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f4e8d69171154cb485c7d7a71d047fb4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -206,7 +225,7 @@ " #n_overlays=5,\n", " #sticky=True,\n", " )\n", - "__ = molpx.visualize.feature(feat.active_features[0], \n", + "__ = molpx.visualize.feature(feat, \n", " mpx_wdg_box.linked_ngl_wdgs[0], \n", " idxs=proj_idxs, radius=.5, \n", " color_list=['red','green'])\n", @@ -223,7 +242,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": { "scrolled": false }, @@ -231,27 +250,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "41da98402f6f4ab296faa44fee8a6369", + "model_id": "b04bd522acca45aa979ef3fbf163085c", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(VBox(children=(Button(description='NGL widgets', layout=Layout(width='100%'), style=ButtonStyle()), VBox(children=(NGLWidget(count=4125),), layout=Layout(border='solid'))), layout=Layout(height='2.0in', width='5.0in')), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -269,7 +273,7 @@ " panel_height=1, \n", " proj_labels='feat',\n", " )\n", - "molpx.visualize.feature(feat.active_features[0], mpx_wdg_box.linked_ngl_wdgs[0], idxs=proj_idxs)\n", + "molpx.visualize.feature(feat, mpx_wdg_box.linked_ngl_wdgs[0], idxs=proj_idxs)\n", "mpx_wdg_box" ] }, @@ -282,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -291,7 +295,7 @@ "('ATOM:PRO 9 CA 8 x', 'ATOM:LYS 15 CA 14 x')" ] }, - "execution_count": 6, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -305,62 +309,61 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8a9a553996a44aa0ab2b34cfe5946941", + "model_id": "669bd2415d00423b99d531b76ab92d0a", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" + "A Jupyter Widget" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:16:35 pyemma.coordinates.clustering.kmeans.KmeansClustering[30] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c7a939e0f10e49d79db78074921df608", + "model_id": "418ca4b4e62042908bcb6cbc4acd6f1f", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=103), FigureCanvasNbAgg()))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8da959d7add54ef58439fd5b7d358b22", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -377,7 +380,7 @@ " proj_labels='feat',\n", " #n_overlays=5,\n", " )\n", - "molpx.visualize.feature(feat.active_features[0], mpx_wdg_box.linked_ngl_wdgs[0], idxs=proj_idxs, color_list=['red','green'])\n", + "molpx.visualize.feature(feat, mpx_wdg_box.linked_ngl_wdgs[0], idxs=proj_idxs, color_list=['red','green'])\n", "mpx_wdg_box" ] }, @@ -390,41 +393,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 11, "metadata": { + "collapsed": true, "scrolled": false }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d0f597ac9a85431fa6600c772d4520c9", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], - "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "from os.path import exists\n", "top = molpx._molpxdir(join='notebooks/data/ala2.pdb')\n", @@ -446,33 +420,40 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "431f665cb3c2452094c78ec3ccc0503b", + "model_id": "465d9173d4864fe58cba25ca2b1fe31d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:17:16 pyemma.coordinates.clustering.kmeans.KmeansClustering[41] INFO Cluster centers converged after 8 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "304c9b268c2b4f38a499d1e25a1f0596", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -488,7 +469,7 @@ " proj_idxs=proj_idxs,\n", " proj_labels=feat, \n", " )\n", - "molpx.visualize.feature(feat.active_features[0], mpx_wdg_box.linked_ngl_wdgs[0], idxs=[0], radius=.5)\n", + "molpx.visualize.feature(feat, mpx_wdg_box.linked_ngl_wdgs[0], idxs=[0], radius=.5)\n", "mpx_wdg_box" ] } @@ -510,7 +491,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" }, "widgets": { "state": { diff --git a/molpx/notebooks/2.molPX_TICA_BPTI.ipynb b/molpx/notebooks/2.molPX_TICA_and_MSMs_BPTI.ipynb similarity index 51% rename from molpx/notebooks/2.molPX_TICA_BPTI.ipynb rename to molpx/notebooks/2.molPX_TICA_and_MSMs_BPTI.ipynb index 9331ab6..b776bc0 100644 --- a/molpx/notebooks/2.molPX_TICA_BPTI.ipynb +++ b/molpx/notebooks/2.molPX_TICA_and_MSMs_BPTI.ipynb @@ -32,10 +32,8 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 2, + "metadata": {}, "outputs": [], "source": [ "top = 'notebooks/data/bpti-c-alpha_centered.pdb'\n", @@ -47,8 +45,7 @@ "dt = 244 #saving interval in the .xtc files, in ns\n", "\n", "import molpx\n", - "from matplotlib import pylab as plt\n", - "%matplotlib ipympl\n", + "from matplotlib import pyplot as plt\n", "import pyemma\n", "import numpy as np\n", "\n", @@ -66,10 +63,8 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "# Create a memory representation of the trajectories\n", @@ -89,10 +84,8 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ "# Perform TICA or read from file directly if already .npy-files exist\n", @@ -122,11 +115,69 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b93db910c62043b1822f2da274e45bb0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:04 pyemma.coordinates.clustering.kmeans.KmeansClustering[0] INFO Cluster centers converged after 6 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6df52f04ead341aab7b20d98f91eb0d7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "256408dce1b44035854355e92d01b09e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "mpx_wdg_box = molpx.visualize.FES(MD_list, \n", " #MD_trajfiles, \n", @@ -157,11 +208,90 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "39be6324f1e54066a228104bc20fa991", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "11437cf4759f4cebb10127ed094ce173", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:10 pyemma.coordinates.clustering.kmeans.KmeansClustering[10] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "00163b8d1ac648629531b16d9c8eadfe", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ed24cba5be9f4813b1e976916f04bb4c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "mpx_wdg_box = molpx.visualize.traj(MD_trajfiles, \n", " top, \n", @@ -192,9 +322,85 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "974210f2d77a4033828f91883f705eb3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8adfc977e5494497bce705a331d237f3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:15 pyemma.coordinates.clustering.kmeans.KmeansClustering[19] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ad74ec33d703455bbc8297b642246bf9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "text/plain": [ + "((203, 2),\n", + " )" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data_sample, geoms = molpx.generate.sample(#MD_list, \n", " MD_trajfiles, \n", @@ -217,11 +423,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6f42f00335bb40bab5b36ddcd3013237", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mi/gph82/miniconda3/lib/python3.6/site-packages/ipykernel_launcher.py:4: RuntimeWarning: divide by zero encountered in log\n", + " after removing the cwd from sys.path.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1e16537f6b2542b9bd40e698449303e4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Replot the FES\n", "plt.figure(figsize=(7,7))\n", @@ -248,9 +491,117 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "72b81eff927046788368e764761364f2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9ad556c7f8b34f15bab0502b97bc9253", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:20 pyemma.coordinates.clustering.kmeans.KmeansClustering[30] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "bb4c74d060e44891a91d7eb018c3d9b1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1ccb4d20d14e442f9dfd90ff2724a416", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:26 pyemma.coordinates.clustering.kmeans.KmeansClustering[37] INFO Cluster centers converged after 11 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "01e70ca23c534d1bba761237e8ed1ed3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + } + ], "source": [ "paths_dict, idata = molpx.generate.projection_paths(#MD_list, \n", " MD_trajfiles, \n", @@ -275,10 +626,8 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "execution_count": 10, + "metadata": {}, "outputs": [], "source": [ "# Choose the coordinate and the tyep of path\n", @@ -295,11 +644,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cea24390e26045d7b2ea7060d7618df1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mi/gph82/miniconda3/lib/python3.6/site-packages/ipykernel_launcher.py:3: RuntimeWarning: divide by zero encountered in log\n", + " This is separate from the ipykernel package so we can avoid doing imports until\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1fa71b389f3d46e1ade55939c28befdd", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "plt.figure(figsize=(7,7))\n", "h, (x,y) = np.histogramdd(np.vstack(Y)[:,proj_idxs], bins=50)\n", @@ -329,9 +715,73 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d72f71f0a2fb421ab0d3dc7cee6e8a65", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f0e14a9f44aa4ad29b4be25e91676e2d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "562c71a39bba464dab6ef3019fab5749", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + } + ], "source": [ "# Re-do the TICA computation to make sure we have a tica object in memory\n", "feat = pyemma.coordinates.featurizer(top)\n", @@ -351,9 +801,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c342c71b102a4c6284f3cd852d652277", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Comment or uncomment the optinal parameters and see how the method reacts\n", "# You can use a pre-instantiated the widget\n", @@ -371,22 +836,156 @@ "ngl_wdg" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the correlation-dictionary's modified `print` function to see what's inside in a human-friendly way" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Correlation dictionary for 3 projections\n", + " Corr[proj_0|feat] = 0.8\n", + " DIST: PRO 9 CA 8 - TYR 23 CA 22\n", + " feat nr. 112, atom idxs [ 8 22]\n", + " Corr[proj_0|feat] = 0.7\n", + " DIST: PRO 9 CA 8 - TYR 21 CA 20\n", + " feat nr. 111, atom idxs [ 8 20]\n", + " Corr[proj_0|feat] = 0.7\n", + " DIST: PRO 9 CA 8 - LEU 29 CA 28\n", + " feat nr. 115, atom idxs [ 8 28]\n", + "\n", + " Corr[proj_1|feat] = 0.7\n", + " DIST: PRO 9 CA 8 - LYS 15 CA 14\n", + " feat nr. 108, atom idxs [ 8 14]\n", + " Corr[proj_1|feat] = 0.7\n", + " DIST: LYS 15 CA 14 - TYR 23 CA 22\n", + " feat nr. 178, atom idxs [14 22]\n", + " Corr[proj_1|feat] = 0.6\n", + " DIST: LYS 15 CA 14 - PHE 33 CA 32\n", + " feat nr. 183, atom idxs [14 32]\n", + "\n", + " Corr[proj_2|feat] = 0.6\n", + " DIST: THR 11 CA 10 - GLY 37 CA 36\n", + " feat nr. 142, atom idxs [10 36]\n", + " Corr[proj_2|feat] = -0.6\n", + " DIST: PRO 13 CA 12 - GLN 31 CA 30\n", + " feat nr. 161, atom idxs [12 30]\n", + " Corr[proj_2|feat] = -0.6\n", + " DIST: PRO 13 CA 12 - PHE 33 CA 32\n", + " feat nr. 162, atom idxs [12 32]\n", + "\n", + "\n" + ] + } + ], + "source": [ + "print(corr)" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also, `molpx.visualize.traj` can help in visualizing these correlations by parsing along the tica object itself as `projection=tica`. In the next cell, can you spot the differences:\n", "* In the nglwidget?\n", - "* In the trajectories?" + "* In the trajectories?\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "65b233004ff94b50898fab38b6f0c8cb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5a2dc05b2e8a400a896351e4b94c23bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:37 pyemma.coordinates.clustering.kmeans.KmeansClustering[51] INFO Cluster centers converged after 10 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e52ab46d051d476790f404609676d032", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e4e850af19134552807b4ac5f7cb273f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Reuse the visualize.traj method with the tica object as input\n", "mpx_wdg_box = molpx.visualize.traj(MD_trajfiles, \n", @@ -418,9 +1017,46 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dce09a99533e49abbd4e74792e062ebb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f82ae020d8434492a020cdbe2a09e84d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:40 pyemma.coordinates.clustering.kmeans.KmeansClustering[60] INFO Algorithm did not reach convergence criterion of 1e-05 in 10 iterations. Consider increasing max_iter.\n", + "\r" + ] + } + ], "source": [ "# Do \"some\" clustering\n", "clkmeans = pyemma.coordinates.cluster_kmeans([iY[:,:2] for iY in Y], 5)" @@ -428,9 +1064,52 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8e36da50b0dd4ee1b41308d8ab785784", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "06109eae31b54fdfb18deb63d049b389", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + } + ], "source": [ "data_sample, geoms = molpx.generate.sample(MD_trajfiles, top, clkmeans, \n", " n_geom_samples=50, \n", @@ -440,9 +1119,42 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8a1713fbf25f48a98f4e4a06425242a6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mi/gph82/miniconda3/lib/python3.6/site-packages/ipykernel_launcher.py:6: RuntimeWarning: divide by zero encountered in log\n", + " \n" + ] + }, + { + "data": { + "text/plain": [ + "(NGLWidget(count=5), )" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Plot clusters\n", "plt.figure(figsize=(4,4))\n", @@ -466,52 +1178,141 @@ "metadata": {}, "source": [ "## Visual representations for MSMs\n", - "Visually inspect the network behind an MSM" + "Visually inspect the network behind an MSM by coarse graining it to an HMM" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0feee8230d324ad5ac0487713612bae5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d5c7ca2d145d4d96a5b4068a8d98fd49", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "30-05-18 17:43:43 pyemma.coordinates.clustering.kmeans.KmeansClustering[63] INFO Cluster centers converged after 10 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a61ea20e54874a118e9107a4027f2339", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r" + ] + } + ], "source": [ - "MSM = pyemma.msm.estimate_markov_model(clkmeans.dtrajs, 20)" + "clkmeans = pyemma.coordinates.cluster_kmeans([iY[:,:2] for iY in Y], 100)\n", + "MSM = pyemma.msm.estimate_markov_model(clkmeans.dtrajs, 1)\n", + "MSMcg = MSM.coarse_grain(3)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "plt.figure(figsize=(4,4))\n", - "\n", - "ax, pos = pyemma.plots.plot_markov_model(MSM.P, \n", - " minflux=5e-4, \n", - " arrow_labels=None,\n", - " ax=plt.gca(), \n", - " arrow_curvature = 2, show_frame=True,\n", - " pos=clkmeans.clustercenters)\n", - "# Add a background if wanted\n", - "h, (x, y) = np.histogramdd(np.vstack(Y)[:,:2], weights=np.hstack(MSM.trajectory_weights()), bins=50)\n", - "plt.contourf(x[:-1], y[:-1], -np.log(h.T), cmap=\"jet\", alpha=.5, zorder=0)\n", - "plt.xlim(x[[0,-1]])\n", - "plt.xticks(np.unique(x.round()))\n", - "plt.yticks(np.unique(y.round()))\n", - "\n", - "plt.ylim(y[[0,-1]])\n", - "\n", - "linked_ngl_wd, linked_ax_wd = molpx.visualize.sample(pos, geoms, plt.gca(), dot_color='blue')\n", - "linked_ngl_wd" + "from imp import reload\n", + "from molpx import visualize\n", + "reload(visualize)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1af06d7323f342078943825a30d25488", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mpxbox = molpx.visualize.MSM(MSMcg, src, \n", + " pos=clkmeans.clustercenters,\n", + " #sticky=True, \n", + " sharpen=True, \n", + " n_overlays=10,\n", + " figpadding=.5,\n", + " #proj_idxs =[1,0]\n", + " panelsize=6\n", + " )\n", + "mpxbox" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## TPT Reactive Pathway Representation" + "## TPT Reactive Pathway Representation\n", + "Until we add a method for doing this explicitly, you can do it in a few lines of code" ] }, { @@ -529,9 +1330,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Use this object to sample geometries\n", @@ -557,9 +1356,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create a TPT object with most_pop, least_pop as source, sink respectively\n", @@ -571,7 +1368,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true, "scrolled": true }, "outputs": [], @@ -599,9 +1395,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } @@ -623,7 +1417,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" }, "widgets": { "state": { diff --git a/molpx/notebooks/3.molPX_TICA_Ala2.ipynb b/molpx/notebooks/3.molPX_TICA_Ala2.ipynb index 13f3c27..632b9dd 100644 --- a/molpx/notebooks/3.molPX_TICA_Ala2.ipynb +++ b/molpx/notebooks/3.molPX_TICA_Ala2.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": { "collapsed": true, "scrolled": true @@ -23,9 +23,7 @@ "source": [ "from os.path import exists\n", "import molpx\n", - "from matplotlib import pylab as plt\n", - "%matplotlib ipympl\n", - "\n", + "from matplotlib import pyplot as plt\n", "import pyemma\n", "import numpy as np" ] @@ -39,7 +37,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": { "collapsed": true }, @@ -64,39 +62,11 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e81a14207be54f7faf3f685940a85863", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], - "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], "source": [ "feat = pyemma.coordinates.featurizer(top)\n", "feat.add_backbone_torsions()\n", @@ -114,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": { "scrolled": false }, @@ -122,27 +92,34 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "415deb3f4353423b87629d4233d2a651", + "model_id": "c0116accc78044a19e78dc903d7a6714", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 18:03:14 pyemma.coordinates.clustering.kmeans.KmeansClustering[1] INFO Cluster centers converged after 9 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "01397594f745427b80e6fe228811a7b9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -174,7 +151,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": { "scrolled": false }, @@ -182,27 +159,34 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f9ccc4547b6f4485b6151424a17497bc", + "model_id": "eab14edbbedd4e938b0b6bcaff4f8a39", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:57:00 pyemma.coordinates.clustering.kmeans.KmeansClustering[10] INFO Cluster centers converged after 8 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fd8492f500e3491d875b01377127d2a4", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXVBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXVBox(children=(MolPXHBox(children=(VBox(children=(Button(description='NGL widgets', layout=Layout(width='100%'), style=ButtonStyle()), VBox(children=(NGLWidget(count=3334),), layout=Layout(border='solid'))), layout=Layout(height='4.0in', width='5.0in')), FigureCanvasNbAgg())), MolPXHBox(children=(NGLWidget(count=101), FigureCanvasNbAgg()))))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -236,11 +220,54 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f0a39610ce0941eabac5fe0b7e476ae0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:57:13 pyemma.coordinates.clustering.kmeans.KmeansClustering[19] INFO Cluster centers converged after 7 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ee3c7af803c84881aac36bf04b173caf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:57:25 pyemma.coordinates.clustering.kmeans.KmeansClustering[24] INFO Cluster centers converged after 6 steps.\n", + "\r" + ] + } + ], "source": [ "paths_dict, idata = molpx.generate.projection_paths(MD_trajfiles, \n", " top, \n", @@ -255,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 7, "metadata": { "collapsed": true }, @@ -275,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "metadata": { "scrolled": false }, @@ -291,27 +318,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "01623d70d8e844b1bff091673b855024", + "model_id": "fd40a5458fb34089a98b011394ddd2ae", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type HBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "HBox(children=(NGLWidget(count=45), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, @@ -349,74 +361,16 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "01-11-17 21:01:44 pyemma.coordinates.data.featurization.featurizer.MDFeaturizer[7] WARNING The 1D arrays input for add_distances() have been sorted, and index duplicates have been eliminated.\n", + "22-05-18 17:57:50 pyemma.coordinates.data.featurization.featurizer.MDFeaturizer[30] WARNING The 1D arrays input for add_distances() have been sorted, and index duplicates have been eliminated.\n", "Check the output of describe() to see the actual order of the features\n" ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "159e3055ae0549a4a930aa5cc0c2e51f", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], - "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "eec03e0f5b7549a7aabf2bd8975d3ad9", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "

Failed to display Jupyter Widget of type Box.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], - "text/plain": [ - "Box(children=(Text(value=''), IntProgress(value=0)))" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ @@ -430,33 +384,40 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a025b63fc12e4543aa177685c415ebd0", + "model_id": "6aa5805b774f4d6581dccbf2a42aa208", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=103), FigureCanvasNbAgg()))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:57:54 pyemma.coordinates.clustering.kmeans.KmeansClustering[34] INFO Cluster centers converged after 10 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "13dfdb4c0d834ba19fcf8f51b7a358d6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -477,33 +438,40 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "03bb2243ab7843fe9a80deb64574cfe9", + "model_id": "7f7190815ded4204b5c686eff467746b", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXVBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXVBox(children=(MolPXHBox(children=(VBox(children=(Button(description='NGL widgets', layout=Layout(width='100%'), style=ButtonStyle()), VBox(children=(NGLWidget(count=3334),), layout=Layout(border='solid'))), layout=Layout(height='8.0in', width='5.0in')), FigureCanvasNbAgg())), MolPXHBox(children=(NGLWidget(count=103), FigureCanvasNbAgg()))))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:58:17 pyemma.coordinates.clustering.kmeans.KmeansClustering[43] INFO Cluster centers converged after 8 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4f755a7aa0cf4b69b633789b2c0d3bad", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -526,11 +494,54 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "05acfdfb30ed47a7ace60534c7a9384a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:59:02 pyemma.coordinates.clustering.kmeans.KmeansClustering[65] INFO Cluster centers converged after 6 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "298bcecb731d4c80b4c45149d005faf5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:59:13 pyemma.coordinates.clustering.kmeans.KmeansClustering[74] INFO Cluster centers converged after 6 steps.\n", + "\r" + ] + } + ], "source": [ "paths_dict, idata = molpx.generate.projection_paths(MD_trajfiles, \n", " top, \n", @@ -545,7 +556,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 14, "metadata": { "collapsed": true }, @@ -565,33 +576,18 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "168cb4a131314925a023a303cf99ab35", + "model_id": "384eba18599541d3b5d0ae4f6b0bcbe9", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type FigureCanvasNbAgg.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "FigureCanvasNbAgg()" + "A Jupyter Widget" ] }, "metadata": {}, @@ -609,27 +605,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ce80fff3cb8b419a946e33c58a31d4be", + "model_id": "db84a4cd262049f99fcf0968733740d3", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type NGLWidget.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "NGLWidget(count=45)" + "A Jupyter Widget" ] }, "metadata": {}, @@ -653,15 +634,6 @@ "linked_wdg.center_view()\n", "linked_wdg" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] } ], "metadata": { @@ -681,7 +653,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.2" }, "widgets": { "state": { diff --git a/molpx/notebooks/4.molPX_metadynamics_Di-Ala.ipynb b/molpx/notebooks/4.molPX_metadynamics_Di-Ala.ipynb index 629ce56..19919b8 100644 --- a/molpx/notebooks/4.molPX_metadynamics_Di-Ala.ipynb +++ b/molpx/notebooks/4.molPX_metadynamics_Di-Ala.ipynb @@ -26,7 +26,6 @@ "source": [ "import molpx\n", "import numpy as np\n", - "%matplotlib ipympl\n", "\n", "# Topology\n", "top = molpx._molpxdir(join='notebooks/data/ala2.pdb')\n", @@ -45,33 +44,40 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "660eb857cfe240619ced4a609a29f3e2", + "model_id": "678c2332efbd4dc88d97a458f5d1e56a", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(NGLWidget(count=200), FigureCanvasNbAgg()))" + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-05-18 17:27:07 pyemma.coordinates.clustering.kmeans.KmeansClustering[0] INFO Cluster centers converged after 7 steps.\n", + "\r" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "315ea7c3c69541c4b225ccfb2298c0ea", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" ] }, "metadata": {}, @@ -100,7 +106,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": { "scrolled": false }, @@ -108,27 +114,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ec4524caaf9548889d167e1b7b477c5a", + "model_id": "c3cb6ef00f854d0bbe27b8125ff8e637", "version_major": 2, "version_minor": 0 }, - "text/html": [ - "

Failed to display Jupyter Widget of type MolPXHBox.

\n", - "

\n", - " If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n", - " that the widgets JavaScript is still loading. If this message persists, it\n", - " likely means that the widgets JavaScript library is either not installed or\n", - " not enabled. See the Jupyter\n", - " Widgets Documentation for setup instructions.\n", - "

\n", - "

\n", - " If you're reading this message in another notebook frontend (for example, a static\n", - " rendering on GitHub or NBViewer),\n", - " it may mean that your frontend doesn't currently support widgets.\n", - "

\n" - ], "text/plain": [ - "MolPXHBox(children=(VBox(children=(Button(description='NGL widgets', layout=Layout(width='100%'), style=ButtonStyle()), VBox(children=(NGLWidget(count=2001),), layout=Layout(border='solid')), VBox(children=(NGLWidget(count=2001),), layout=Layout(border='solid'))), layout=Layout(height='6.0in', width='5.0in')), FigureCanvasNbAgg()))" + "A Jupyter Widget" ] }, "metadata": {}, diff --git a/molpx/notebooks/5.molPX_GPCR_Opsin_contacts.ipynb b/molpx/notebooks/5.molPX_GPCR_Opsin_contacts.ipynb new file mode 100644 index 0000000..8e6a730 --- /dev/null +++ b/molpx/notebooks/5.molPX_GPCR_Opsin_contacts.ipynb @@ -0,0 +1,3586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# molPX Opsin Example\n", + "
 \n",
+    "Guillermo PĂ©rez-HernĂ¡ndez  guille.perez@fu-berlin.de \n",
+    "
\n", + " \n", + "This notebook uses a very short trajectory of a G-Protein-Coupled-Receptor (GPCR) molecule as an example. \n", + "Unlike Di-Alanine, it is very frequent that such systems are analyzed using of [contact maps](https://en.wikipedia.org/wiki/Protein_contact_map).\n", + "\n", + "This can be done now interactively inside the notebook. The trajectory itself is meaningless, but it still can be used as an example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "import molpx" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start from files on disk\n", + "As usual, our starting point is a pair files on disk with a topology and a trajectory. We import those into the notebook using the awesome [mdtraj](www.mdtraj.org):" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import mdtraj as md\n", + "top = md.load(molpx._molpxdir(join='notebooks/data/ops.pdb.gz')).top\n", + "geom = md.load(molpx._molpxdir(join='notebooks/data/ops_mini.xtc'), top=top)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compute a contact map using `mdtraj`\n", + "As usual, one could have computed this using any other python module or even an external program and load it into the notebook. We use the method [`md.compute_contacts`](http://mdtraj.org/1.9.0/api/generated/mdtraj.compute_contacts.html) and then `md.geometry.squareform` (see the example in the previous link).\n", + "\n", + "We also implement a cutoff of 3.5 Angstrom to create a binary plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "contact_map = md.geometry.squareform(*md.compute_contacts(geom))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "cutoff = .35 # cutoff for residue contact in nm\n", + "contact_map = (contact_map<.35).astype(float)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize the `contact_map`-trajectory interactively\n", + "The following cell generates a matplotlib plot showing the contact map and an NGLView widget. The interactivity consists in:\n", + " - sliding the advance bar in the widget will update the contact map\n", + " - left-clicking on the contact map will:\n", + " - highlight the clicked pixel\n", + " - draw the corresponding distance on the widget\n", + " - right-clicking again on the contact map will delete the distance on the widgtet\n", + "\n", + "Panning and zooming of the contact map is allowed. Uncoment the `average` option to see what happens. As usual, both the plot and the widget are contained in a `molpxHBox`. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "78b54deed26947ea84b13d30213b7099", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "A Jupyter Widget" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mpx_wdg_box = molpx.visualize.contacts(contact_map, geom, \n", + " average=True, \n", + " )\n", + "mpx_wdg_box" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The ```mpx_wdg_box```\n", + "It is a class derived from the ipython widgets [HBox](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html?highlight=Hbox#Container/Layout-widgets) and [VBox](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html?highlight=Hbox#Container/Layout-widgets), with molPX's extra information as attributes starting with `linked_*` " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "linked_ax_wdgs\n", + "linked_axes\n", + "linked_data_arrays\n", + "linked_figs\n", + "linked_mdgeoms\n", + "linked_ngl_wdgs\n" + ] + } + ], + "source": [ + "for attr in dir(mpx_wdg_box):\n", + " if attr.startswith('linked_'):\n", + " print(attr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.2" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "02f12bd6d6504b0ba027f64e0cfde1b1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_7bfd978d08ec442eb49b810fa764620a" + ], + "layout": "IPY_MODEL_04b8decd164b41e99c638cae7b722702" + } + }, + "04b8decd164b41e99c638cae7b722702": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "border": "solid" + } + }, + "057a8469d4854abda77edc709739ca6d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_2e1980f0cec742dab313f9ba0c99c926", + "IPY_MODEL_81ee8894b8b4442b978e319dc3a08da3" + ], + "layout": "IPY_MODEL_a0d945adcbde4207a8dfe8d9b9f371e2" + } + }, + "05d2da84e09d478dab3dee3c72ca6b59": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "05e2b4b8f40e4ba29f0917bbdb4d639e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "07c691224682492db5979c86a01ede66": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "083b5b795e9f4e60a8dfd2abba86db3c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_487200083f724442a5117f01b84eb91d", + "value": {}, + "width": "900.0" + } + }, + "091aba9a11cc45879ac9e3936cdbf397": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "099ccf42fe80479b854da28792ecedeb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "0d98ccbef3e34da180cf96ff2627eeb7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_d5a1720d76c74ef6b4e1d337f268576f", + "value": {}, + "width": "900.0" + } + }, + "0fd19a2883be4e5e808b82f204a3ecdb": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.242729722135532, + 0, + 0, + 0, + 0, + 20.242729722135532, + 0, + 0, + 0, + 0, + 20.242729722135532, + 0, + -1.6345000863075256, + -24.057000160217285, + -0.8634999841451645, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_9399b24089f54498a9e100a5e810ac82", + "n_components": 1, + "picked": {} + } + }, + "142df587b15c41bf9b90c1c858cf5c9f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "1850213735f14c1cb59b177e30d1b5ec": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "1c056b4b60a6442c918b60951d157199": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "1c7f85966a9e4ade86ed57e517a49946": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "2198a2fb7aad45679758cabf172ecc04": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "25fa5915d3d24a82a6f4ed61ba9bd7e2": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_7f1d2053edda4e5ca1b524e9f5924446" + } + }, + "260cabd9f2e442df94234fdeed71bb5f": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_ef6f87771fbe4759a4fa1ed6a1657388" + } + }, + "2e1980f0cec742dab313f9ba0c99c926": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_bd2f9d6498f74a9bbe67da0a7b493466", + "IPY_MODEL_3c0fcc5000af42fa9011307f57d74de0" + ], + "layout": "IPY_MODEL_7bdfa9becaf64de6bf5d8e726cb08a1d" + } + }, + "2f4df660fff844c8ab5a0b81908629e5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_05d2da84e09d478dab3dee3c72ca6b59", + "value": {}, + "width": "900.0" + } + }, + "31b91051b3ec49cda634bde2e7c0d92f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "3291e228d2e94e2d9e8fa6982aa4177f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_a61b2b49969448c0a122dc31812b203c", + "IPY_MODEL_8f3c995b40f94aeb906ceabb0c7edbe6" + ], + "layout": "IPY_MODEL_6030098920544b99896c0bc0a261f659" + } + }, + "3378fb06740f47eebb58aaec872c1190": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "348b8044a47545eb9da6ffd3780fcb58": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_0fd19a2883be4e5e808b82f204a3ecdb", + "IPY_MODEL_adaf51e6a3fc417e914db69ac89b336c" + ], + "layout": "IPY_MODEL_7034794fd65c481baffe5d4cd405082f" + } + }, + "3c0fcc5000af42fa9011307f57d74de0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_c95ca5fe005344588b3a1100a65530a2" + ], + "layout": "IPY_MODEL_9fd08b869f7a4dfda172448bfe845f6c" + } + }, + "3f91662dd21548e7a8a1d095a3701b16": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.261130760158405, + 0, + 0, + 0, + 0, + 20.261130760158405, + 0, + 0, + 0, + 0, + 20.261130760158405, + 0, + -1.602999985218048, + -24.08549976348877, + -0.7745000571012497, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_cb2ce9cb14d74e6eba74aa41e3d3902c", + "n_components": 1, + "picked": {} + } + }, + "40260d55b7e84c5f84ce7ee1658cc7d8": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_099ccf42fe80479b854da28792ecedeb", + "value": {}, + "width": "900.0" + } + }, + "4224777922c14f31894db65575a86dca": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "425f648935634967b4fe500e1c1a2253": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_d523002ca8a94346b19d51176c999b2f", + "value": {}, + "width": "900.0" + } + }, + "4598d04e9e5f4177980ce355c95007e2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_4aeee03c9ad04073910a9ae57df1cd63", + "IPY_MODEL_260cabd9f2e442df94234fdeed71bb5f" + ], + "layout": "IPY_MODEL_c08e3c0d48a848048a9eddb863b63b9b" + } + }, + "4829969606c0466883452a51a9eda8ed": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 22.27569735577003, + 0, + 0, + 0, + 0, + 22.27569735577003, + 0, + 0, + 0, + 0, + 22.27569735577003, + 0, + -1.6459999680519104, + -24.449999809265137, + -0.7815000414848328, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 93, + "layout": "IPY_MODEL_c492ce1d58cd4004a095f6c0aefd7d04", + "n_components": 1, + "picked": {} + } + }, + "487200083f724442a5117f01b84eb91d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "4aeee03c9ad04073910a9ae57df1cd63": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 22.335864075149413, + 0, + 0, + 0, + 0, + 22.335864075149413, + 0, + 0, + 0, + 0, + 22.335864075149413, + 0, + -1.5269999504089355, + -24.34749984741211, + -0.7614999413490295, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_952e7c4f72be4ad78e081ef9ca637c2a", + "n_components": 1, + "picked": {} + } + }, + "4b8ff78d08724692bffc261489797bcd": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.440059597101534, + 0, + 0, + 0, + 0, + 20.440059597101534, + 0, + 0, + 0, + 0, + 20.440059597101534, + 0, + -1.5479999482631683, + -24.090499877929688, + -0.8869999498128891, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_981cfe953700402498eeec039f102f4a", + "n_components": 1, + "picked": {} + } + }, + "4e2d7923d0e34c7fbf272cab480aabc7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "4f5f5b9396204953a477bf57fbccbfc0": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "526cafeb1ce042c1bce2614988570e74": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "56ca44e8b4ef41eeace7b3c88ec9b15f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_2198a2fb7aad45679758cabf172ecc04", + "value": {}, + "width": "900.0" + } + }, + "5ce550eaef3f470a9d6e6142a846f97d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "6030098920544b99896c0bc0a261f659": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "643eba7fb1e545d295d4b46e07b96d34": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_3f91662dd21548e7a8a1d095a3701b16", + "IPY_MODEL_87ba27ec276d4b4b9ac32cfdbc0d42fe" + ], + "layout": "IPY_MODEL_64c8a781e0f843b9a047200d2a4b844b" + } + }, + "64c8a781e0f843b9a047200d2a4b844b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "6775e7024caa458ea43e223a7fea355a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ButtonStyleModel", + "state": {} + }, + "67b93a07aeea405daccd6c55cf6ea5ef": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_fb9d851ef63e4813bc8dcb22dc7e0f4b", + "IPY_MODEL_c6030751df7c46a2b1ba0b1e303c79cd" + ], + "layout": "IPY_MODEL_4f5f5b9396204953a477bf57fbccbfc0" + } + }, + "69ed4cec545c401b88f9dd9921011751": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_4829969606c0466883452a51a9eda8ed", + "IPY_MODEL_d5f60915434a422999c82949b45700a6" + ], + "layout": "IPY_MODEL_ca6b587f4bdf4a31ac25c181de986d10" + } + }, + "6ce05eb037e14bb18eca09f8ea356484": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_1c7f85966a9e4ade86ed57e517a49946" + } + }, + "7034794fd65c481baffe5d4cd405082f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "7311e30372fd4ad99110f011b8adb856": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "74f0b5162cd042ff829d4578becfdb24": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_07c691224682492db5979c86a01ede66", + "value": {}, + "width": "900.0" + } + }, + "75807289a5e64c8eb4e8179d222dfc8f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "75a01831722d4f60b5ee7e5eeca054b7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "7bdfa9becaf64de6bf5d8e726cb08a1d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "height": "2.0in", + "width": "5.0in" + } + }, + "7bfd978d08ec442eb49b810fa764620a": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 23.053436912758006, + 0, + 0, + 0, + 0, + 23.053436912758006, + 0, + 0, + 0, + 0, + 23.053436912758006, + 0, + -1.7549998760223389, + -24.3100004196167, + -0.7849999666213989, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 3334, + "frame": 0, + "layout": "IPY_MODEL_05e2b4b8f40e4ba29f0917bbdb4d639e", + "n_components": 1, + "picked": {} + } + }, + "7c45ca81b410488dad1921ef9e3cd666": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "width": "100%" + } + }, + "7db7fde8386c41f4a6729b9f01fd10fa": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "7f1d2053edda4e5ca1b524e9f5924446": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "806ca048c1834d5a91b01e0384ba0116": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_c30dcd93e9fc4fc780101d81fc128c10", + "value": {}, + "width": "900.0" + } + }, + "80c7306fd5394a8180989ee6d1421468": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "81ee8894b8b4442b978e319dc3a08da3": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_3378fb06740f47eebb58aaec872c1190" + } + }, + "87ba27ec276d4b4b9ac32cfdbc0d42fe": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_7db7fde8386c41f4a6729b9f01fd10fa" + } + }, + "8931ccd404fc4f4c992e0ecd8b9337cc": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "height": "2.0in", + "width": "5.0in" + } + }, + "8a2c5b58347043848a48e53894cb7152": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "width": "100%" + } + }, + "8a9d4e5f727846c183272e24d1c9bd7b": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.25637601878781, + 0, + 0, + 0, + 0, + 20.25637601878781, + 0, + 0, + 0, + 0, + 20.25637601878781, + 0, + -1.5909999758005142, + -23.98050022125244, + -0.5059999823570251, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 50, + "layout": "IPY_MODEL_a41caba466c94b7e8d8f7061dcc1c7a7", + "n_components": 1, + "picked": {} + } + }, + "8ac9a3d696a246d38d7859788311f6f2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "8b61aba639974bdeb2e63e99aaab8f29": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_cbaf382c92d8485892ea4bd1ff093eb7", + "IPY_MODEL_dc6fbb5ce3d44e6baf4fb3e68232ea8a" + ], + "layout": "IPY_MODEL_b199ab3bb19c47f6825419e9a73f7afe" + } + }, + "8baafb4e8bb64a17b429fd94923253a3": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "8db875f339984f819fa6a20484ed64d2": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": {}, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": {}, + "_ngl_repr_dict": {}, + "_ngl_serialize": false, + "_ngl_version": "", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_31b91051b3ec49cda634bde2e7c0d92f", + "n_components": 0, + "picked": {} + } + }, + "8e24b4b7e987488193788f311bbbd1d4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_057a8469d4854abda77edc709739ca6d", + "IPY_MODEL_4598d04e9e5f4177980ce355c95007e2" + ], + "layout": "IPY_MODEL_a00ebdef56f94a2e99f3b224f274a4f1" + } + }, + "9399b24089f54498a9e100a5e810ac82": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "952e7c4f72be4ad78e081ef9ca637c2a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "981cfe953700402498eeec039f102f4a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "9cbc32d8ba4141a3b1aeae20544d8520": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "9e26a9b8cefa4edd8ad1316037e76303": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "9f9e073074c343dd8b5b37995b65d30a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_de0e2051dd0c46068cdb8c3903b7d4f5", + "IPY_MODEL_d2b7ae0272eb4bc1afd3e11566d20589" + ], + "layout": "IPY_MODEL_75807289a5e64c8eb4e8179d222dfc8f" + } + }, + "9fd08b869f7a4dfda172448bfe845f6c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": { + "border": "solid" + } + }, + "a00ebdef56f94a2e99f3b224f274a4f1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "a0d945adcbde4207a8dfe8d9b9f371e2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "a0e170719f6c48618e49d034ea1060d0": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.345096809765252, + 0, + 0, + 0, + 0, + 20.345096809765252, + 0, + 0, + 0, + 0, + 20.345096809765252, + 0, + -1.6269999742507935, + -24.015000343322754, + -0.9159999787807465, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_c3e93c813db24913aee9e7d610ccfe14", + "n_components": 1, + "picked": {} + } + }, + "a41caba466c94b7e8d8f7061dcc1c7a7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "a61b2b49969448c0a122dc31812b203c": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.24317995028008, + 0, + 0, + 0, + 0, + 20.24317995028008, + 0, + 0, + 0, + 0, + 20.24317995028008, + 0, + -1.6150000393390656, + -24.236000061035156, + -0.6120000183582306, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_526cafeb1ce042c1bce2614988570e74", + "n_components": 1, + "picked": {} + } + }, + "aaee598a144e485cad85e2db0dcc99a0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_142df587b15c41bf9b90c1c858cf5c9f", + "value": {}, + "width": "900.0" + } + }, + "adaf51e6a3fc417e914db69ac89b336c": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_4e2d7923d0e34c7fbf272cab480aabc7" + } + }, + "adcc3be2d57d4f748750a6a3e683d26e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_a0e170719f6c48618e49d034ea1060d0", + "IPY_MODEL_25fa5915d3d24a82a6f4ed61ba9bd7e2" + ], + "layout": "IPY_MODEL_091aba9a11cc45879ac9e3936cdbf397" + } + }, + "b199ab3bb19c47f6825419e9a73f7afe": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "b5bebd1b82d74352a5ec74715925d0ce": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_80c7306fd5394a8180989ee6d1421468", + "value": {}, + "width": "900.0" + } + }, + "b6ac057b617e49a4a490ee59ed74328e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "b84977b422434764b1c596cb0d78791b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_ea2bec8bc1ba4d27b1b13f38b06799b7", + "value": {}, + "width": "900.0" + } + }, + "bc01ad74a4cc4f239988a261d2d550b2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "bd2f9d6498f74a9bbe67da0a7b493466": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ButtonModel", + "state": { + "description": "NGL widgets", + "layout": "IPY_MODEL_7c45ca81b410488dad1921ef9e3cd666", + "style": "IPY_MODEL_bf0a3c2e2c4843cf88b8805c872ca3b5" + } + }, + "bed98b43d9c343dab002b228ba726822": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "bf0a3c2e2c4843cf88b8805c872ca3b5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ButtonStyleModel", + "state": {} + }, + "c08e3c0d48a848048a9eddb863b63b9b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "c30dcd93e9fc4fc780101d81fc128c10": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "c3e93c813db24913aee9e7d610ccfe14": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "c492ce1d58cd4004a095f6c0aefd7d04": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "c6030751df7c46a2b1ba0b1e303c79cd": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_4224777922c14f31894db65575a86dca" + } + }, + "c95ca5fe005344588b3a1100a65530a2": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 23.053436912758006, + 0, + 0, + 0, + 0, + 23.053436912758006, + 0, + 0, + 0, + 0, + 23.053436912758006, + 0, + -1.7549998760223389, + -24.3100004196167, + -0.7849999666213989, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 3334, + "frame": 0, + "layout": "IPY_MODEL_bed98b43d9c343dab002b228ba726822", + "n_components": 1, + "picked": {} + } + }, + "ca6b587f4bdf4a31ac25c181de986d10": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "cb2ce9cb14d74e6eba74aa41e3d3902c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "cbaf382c92d8485892ea4bd1ff093eb7": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.16727026960458, + 0, + 0, + 0, + 0, + 20.16727026960458, + 0, + 0, + 0, + 0, + 20.16727026960458, + 0, + -1.670500010251999, + -24.258000373840332, + -0.6459999978542328, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 99, + "layout": "IPY_MODEL_75a01831722d4f60b5ee7e5eeca054b7", + "n_components": 1, + "picked": {} + } + }, + "cbfb5bb0aa114fce88e90ae57fdd123f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_8a9d4e5f727846c183272e24d1c9bd7b", + "IPY_MODEL_880fc32d8fae45ef8c48be9a054db4c4" + ], + "layout": "IPY_MODEL_5ce550eaef3f470a9d6e6142a846f97d" + } + }, + "d2b7ae0272eb4bc1afd3e11566d20589": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_7311e30372fd4ad99110f011b8adb856" + } + }, + "d523002ca8a94346b19d51176c999b2f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "d5a1720d76c74ef6b4e1d337f268576f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "d5f60915434a422999c82949b45700a6": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_bc01ad74a4cc4f239988a261d2d550b2" + } + }, + "d86f951d40324b92af3c0b2ff48a46a3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_8ac9a3d696a246d38d7859788311f6f2", + "value": {}, + "width": "900.0" + } + }, + "dc6fbb5ce3d44e6baf4fb3e68232ea8a": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_f9b88113417c423099ee72fa0e4b961f" + } + }, + "de0e2051dd0c46068cdb8c3903b7d4f5": { + "model_module": "nglview-js-widgets", + "model_module_version": "0.5.4-dev.27", + "model_name": "NGLModel", + "state": { + "_camera_orientation": [ + 20.494602250547747, + 0, + 0, + 0, + 0, + 20.494602250547747, + 0, + 0, + 0, + 0, + 20.494602250547747, + 0, + -1.4594999849796295, + -24.00349998474121, + -0.5250000357627869, + 1 + ], + "_camera_str": "orthographic", + "_image_data": "", + "_n_dragged_files": 0, + "_ngl_coordinate_resource": {}, + "_ngl_full_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_full_stage_parameters_embed": {}, + "_ngl_msg_archive": [], + "_ngl_original_stage_parameters": { + "ambientColor": 14540253, + "ambientIntensity": 0.2, + "backgroundColor": "white", + "cameraFov": 40, + "cameraType": "perspective", + "clipDist": 10, + "clipFar": 100, + "clipNear": 0, + "fogFar": 100, + "fogNear": 50, + "hoverTimeout": 0, + "impostor": true, + "lightColor": 14540253, + "lightIntensity": 1, + "mousePreset": "default", + "panSpeed": 1, + "quality": "medium", + "rotateSpeed": 2, + "sampleLevel": 0, + "tooltip": true, + "workerDefault": true, + "zoomSpeed": 1.2 + }, + "_ngl_repr_dict": { + "0": { + "0": { + "params": { + "aspectRatio": 2, + "assembly": "default", + "bondScale": 0.4, + "bondSpacing": 1, + "clipCenter": { + "x": 0, + "y": 0, + "z": 0 + }, + "clipNear": 0, + "clipRadius": 0, + "colorMode": "hcl", + "colorReverse": false, + "colorScale": "", + "colorScheme": "element", + "colorValue": 9474192, + "cylinderOnly": false, + "defaultAssembly": "", + "depthWrite": true, + "diffuse": 16777215, + "disableImpostor": false, + "disablePicking": false, + "flatShaded": false, + "lazy": false, + "lineOnly": false, + "linewidth": 2, + "matrix": { + "elements": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ] + }, + "metalness": 0, + "multipleBond": "off", + "opacity": 1, + "openEnded": true, + "quality": "medium", + "radialSegments": 10, + "radius": 0.15, + "roughness": 0.4, + "scale": 1, + "sele": "all", + "side": "double", + "sphereDetail": 1, + "visible": true, + "wireframe": false + }, + "type": "ball+stick" + } + } + }, + "_ngl_serialize": false, + "_ngl_version": "1.0.0-beta.4", + "_scene_position": {}, + "_scene_rotation": {}, + "background": "white", + "count": 101, + "frame": 0, + "layout": "IPY_MODEL_8baafb4e8bb64a17b429fd94923253a3", + "n_components": 1, + "picked": {} + } + }, + "e427834063234005be0f3c41f8f7d04e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "HBoxModel", + "state": { + "children": [ + "IPY_MODEL_4b8ff78d08724692bffc261489797bcd", + "IPY_MODEL_ef29a6fa673747908780bc8e8d877019" + ], + "layout": "IPY_MODEL_eabe68947cc9494cba4387ed0fdb84aa" + } + }, + "e5ff550df1784bc698a701c5fcad3490": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_67b93a07aeea405daccd6c55cf6ea5ef", + "IPY_MODEL_69ed4cec545c401b88f9dd9921011751" + ], + "layout": "IPY_MODEL_1c056b4b60a6442c918b60951d157199" + } + }, + "ea2bec8bc1ba4d27b1b13f38b06799b7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "eabe68947cc9494cba4387ed0fdb84aa": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "ef29a6fa673747908780bc8e8d877019": { + "model_module": "jupyter-matplotlib", + "model_module_version": "^0.0.2", + "model_name": "MPLCanvasModel", + "state": { + "_dom_classes": [], + "_id": "", + "_toolbar_items": [ + [ + "Home", + "Reset original view", + "fa fa-home icon-home", + "home" + ], + [ + "Back", + "Back to previous view", + "fa fa-arrow-left icon-arrow-left", + "back" + ], + [ + "Forward", + "Forward to next view", + "fa fa-arrow-right icon-arrow-right", + "forward" + ], + [ + "", + "", + "", + "" + ], + [ + "Pan", + "Pan axes with left mouse, zoom with right", + "fa fa-arrows icon-move", + "pan" + ], + [ + "Zoom", + "Zoom to rectangle", + "fa fa-square-o icon-check-empty", + "zoom" + ], + [ + "", + "", + "", + "" + ], + [ + "Download", + "Download plot", + "fa fa-floppy-o icon-save", + "download" + ], + [ + "Export", + "Export plot", + "fa fa-file-picture-o icon-picture", + "export" + ] + ], + "layout": "IPY_MODEL_9cbc32d8ba4141a3b1aeae20544d8520" + } + }, + "ef6f87771fbe4759a4fa1ed6a1657388": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "f06c86a748a7412890505f0152d6bd72": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ButtonModel", + "state": { + "description": "NGL widgets", + "layout": "IPY_MODEL_8a2c5b58347043848a48e53894cb7152", + "style": "IPY_MODEL_6775e7024caa458ea43e223a7fea355a" + } + }, + "f9b88113417c423099ee72fa0e4b961f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.0.0", + "model_name": "LayoutModel", + "state": {} + }, + "fb9d851ef63e4813bc8dcb22dc7e0f4b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "VBoxModel", + "state": { + "children": [ + "IPY_MODEL_f06c86a748a7412890505f0152d6bd72", + "IPY_MODEL_02f12bd6d6504b0ba027f64e0cfde1b1" + ], + "layout": "IPY_MODEL_8931ccd404fc4f4c992e0ecd8b9337cc" + } + }, + "fc79888efdac446485d6ae522731d719": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.0.0", + "model_name": "ImageModel", + "state": { + "layout": "IPY_MODEL_1850213735f14c1cb59b177e30d1b5ec", + "value": {}, + "width": "900.0" + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/molpx/notebooks/data/ops.pdb.gz b/molpx/notebooks/data/ops.pdb.gz new file mode 100644 index 0000000..e0323f0 Binary files /dev/null and b/molpx/notebooks/data/ops.pdb.gz differ diff --git a/molpx/notebooks/data/ops_mini.xtc b/molpx/notebooks/data/ops_mini.xtc new file mode 100644 index 0000000..219c606 Binary files /dev/null and b/molpx/notebooks/data/ops_mini.xtc differ diff --git a/molpx/tests/test_bmutils.py b/molpx/tests/test_bmutils.py index 43f5ba5..b94e51a 100644 --- a/molpx/tests/test_bmutils.py +++ b/molpx/tests/test_bmutils.py @@ -10,6 +10,9 @@ import mdtraj as md from glob import glob import molpx +from numpy.testing import assert_raises + +from scipy.spatial.distance import pdist as _pdist, squareform as _squareform class TestWithBPTIData(unittest.TestCase): r""" @@ -29,83 +32,61 @@ def setUpClass(self): self.Fs = self.source.get_output() self.tica = pyemma.coordinates.tica(self.source, lag=1, dim=2) - self.pca = pyemma.coordinates.tica(self.source, dim=2) + self.pca = pyemma.coordinates.pca(self.source, dim=2) self.Ys = self.tica.get_output() self.tempdir = tempfile.mkdtemp('test_molpx') - self.projected_files = [os.path.join(self.tempdir, 'Y.%u.npy'%ii) for ii in range(len(self.MD_trajectories))] - [np.save(ifile, iY) for ifile, iY in zip(self.projected_files, self.Ys)] - [np.savetxt(ifile.replace('.npy', '.dat'), iY) for ifile, iY in zip(self.projected_files, self.Ys)] + self.projected_files_npy = [os.path.join(self.tempdir, 'Y.%u.npy' % ii) for ii in range(len(self.MD_trajectories))] + self.projected_files_dat = [ifile.replace(".npy",".dat") for ifile in self.projected_files_npy] + [np.save(ifile, iY) for ifile, iY in zip(self.projected_files_npy, self.Ys)] + [np.savetxt(ifile.replace('.npy', '.dat'), iY) for ifile, iY in zip(self.projected_files_npy, self.Ys)] @classmethod def tearDownClass(self): shutil.rmtree(self.tempdir) -class TestReadingInput(unittest.TestCase): +class TestReadingInput(TestWithBPTIData): - def setUp(self): - self.MD_trajectory = os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_mini.xtc') - self.MD_topology = os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb') - self.tempdir = tempfile.mkdtemp('test_molpx') - self.projected_file = os.path.join(self.tempdir,'Y.npy') - self.feat = pyemma.coordinates.featurizer(self.MD_topology) - self.feat.add_all() - source = pyemma.coordinates.source(self.MD_trajectory, features=self.feat) - self.tica = pyemma.coordinates.tica(source,lag=1, dim=2) - self.Y = self.tica.get_output()[0] - self.F = source.get_output() - np.save(self.projected_file,self.Y) - np.savetxt(self.projected_file.replace('.npy','.dat'),self.Y) + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() - def tearDown(self): - shutil.rmtree(self.tempdir) + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() def test_data_from_input_npy(self): # Just one string - assert np.allclose(self.Y, _bmutils.data_from_input(self.projected_file)[0]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input(self.projected_files_npy)[0]) # List of one string - assert np.allclose(self.Y, _bmutils.data_from_input([self.projected_file])[0]) - # List of two strings - Ys = _bmutils.data_from_input([self.projected_file, - self.projected_file]) - assert np.all([np.allclose(self.Y, iY) for iY in Ys]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input([self.projected_files_npy[0]])) + # List of strings + Ys = _bmutils.data_from_input(self.projected_files_npy) + assert np.all([np.allclose(jY, iY) for jY, iY in zip(self.Ys, Ys)]) def test_data_from_input_throws_exception(self): - try: - _bmutils.data_from_input(np.random.randn(1000)) - except ValueError: - pass + assert_raises(ValueError, _bmutils.data_from_input, 1) def test_data_from_input_ascii(self): # Just one string - assert np.allclose(self.Y, _bmutils.data_from_input(self.projected_file.replace('.npy', '.dat'))[0]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input(self.projected_files_dat)[0]) # List of one string - assert np.allclose(self.Y, _bmutils.data_from_input([self.projected_file.replace('.npy', '.dat')])[0]) - # List of two strings - Ys = _bmutils.data_from_input([self.projected_file.replace('.npy', '.dat'), - self.projected_file.replace('.npy','.dat')]) - assert np.all([np.allclose(self.Y, iY) for iY in Ys]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input([self.projected_files_dat[0]])) + # List of strings + Ys = _bmutils.data_from_input(self.projected_files_dat) + assert np.all([np.allclose(jY, iY) for jY, iY in zip(self.Ys, Ys)]) def test_data_from_input_ndarray(self): # Just one ndarray - assert np.allclose(self.Y, _bmutils.data_from_input(self.Y)[0]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input(self.Ys[0])) # List of one ndarray - assert np.allclose(self.Y, _bmutils.data_from_input([self.Y])[0]) - # List of two ndarray - Ys = _bmutils.data_from_input([self.Y, - self.Y]) - assert np.all([np.allclose(self.Y, iY) for iY in Ys]) - - # Not implemented yet - def _test_data_from_input_ndarray_ascii_npy(self): - # List of everything - Ys = _bmutils.data_from_input([self.projected_file, - self.projected_file.replace('.npy','.dat'), - self.Y]) - assert np.all([np.allclose(self.Y, iY) for iY in Ys]) + assert np.allclose(self.Ys[0], _bmutils.data_from_input([self.Ys[0]])[0]) + # Lists + Ys = _bmutils.data_from_input(self.Ys) + assert np.all([np.allclose(jY, iY) for jY,iY in zip(self.Ys, Ys)]) def test_moldata_from_input(self): # Traj and top strings - moldata = _bmutils.moldata_from_input(self.MD_trajectory, MD_top=self.MD_topology) + moldata = _bmutils.moldata_from_input(self.MD_trajectory_files, MD_top=self.MD_topology) assert isinstance(moldata, _bmutils._FeatureReader) # Source object directly @@ -113,27 +94,79 @@ def test_moldata_from_input(self): assert isinstance(moldata, _bmutils._FeatureReader) # Typerror: - try: - _bmutils.moldata_from_input(11) - except TypeError: - pass + assert_raises(TypeError, _bmutils.moldata_from_input, 11) # List of trajectories - geom = md.load(self.MD_trajectory, top=self.MD_topology) - moldata = _bmutils.moldata_from_input(geom) + moldata = _bmutils.moldata_from_input(self.MD_trajectories) assert isinstance(moldata[0], md.Trajectory), moldata def test_assert_moldata_belong_data(self): # Traj vs data - geom = md.load(self.MD_trajectory, top=self.MD_topology) - _bmutils.assert_moldata_belong_data([geom], [self.Y]) + _bmutils.assert_moldata_belong_data(self.MD_trajectories, self.Ys) # src vs data - moldata = _bmutils.moldata_from_input(self.MD_trajectory, MD_top=self.MD_topology) - _bmutils.assert_moldata_belong_data(moldata, [self.Y]) + moldata = _bmutils.moldata_from_input(self.source, MD_top=self.MD_topology) + + _bmutils.assert_moldata_belong_data(moldata, self.Ys) + #print(moldata, moldata.number_of_trajectories(), moldata.trajectory_lengths()) # With stride - _bmutils.assert_moldata_belong_data([geom], [iY[::5] for iY in [self.Y]], data_stride=5) + _bmutils.assert_moldata_belong_data(self.MD_trajectories, [iY[::5] for iY in self.Ys], data_stride=5) + +class TestSaveTraj(TestWithBPTIData): + + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + self.samples = [[0, 10], + [1, 20], + [2, 30]] + + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + + + def test_just_works(self): + geoms_ref = pyemma.coordinates.save_traj(self.source, self.samples, None) + geoms_molpx = _bmutils.save_traj_wrapper(self.source, self.samples, None) + assert np.all([np.allclose(ixyz, jxyz) for ixyz, jxyz in zip(geoms_ref.xyz, geoms_molpx.xyz)]) + + def test_works_with_MDTrajectories(self): + + geoms_ref = pyemma.coordinates.save_traj(self.source, self.samples, None) + geoms_molpx = _bmutils.save_traj_wrapper(self.MD_trajectories, self.samples, None) + assert np.all([np.allclose(ixyz, jxyz) for ixyz, jxyz in zip(geoms_ref.xyz, geoms_molpx.xyz)]) + + def test_works_with_MDTrajectories_with_stride(self): + geoms_ref = pyemma.coordinates.save_traj(self.source, self.samples, None, stride=2) + geoms_molpx = _bmutils.save_traj_wrapper(self.MD_trajectories, self.samples, None, stride=2) + assert np.all([np.allclose(ixyz, jxyz) for ixyz, jxyz in zip(geoms_ref.xyz, geoms_molpx.xyz)]) + + def test_raises(self): + assert_raises(TypeError, _bmutils.save_traj_wrapper, 1, self.samples, None) + +class TestCorrelations(TestWithBPTIData): + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + + def test_input_types(self): + _bmutils.most_corr(self.tica) + _bmutils.most_corr(self.pca) + _bmutils.most_corr(self.feat) + _bmutils.most_corr(self.tica.feature_TIC_correlation) + _bmutils.most_corr(self.tica.feature_TIC_correlation, feat_name="My CustomFeature") + + def test_fails(self): + assert_raises(TypeError, _bmutils.most_corr, "a") + + def test_printing(self): + print(_bmutils.most_corr(self.tica)) def test_most_corr_info_works(self): most_corr = _bmutils.most_corr(self.tica) @@ -156,8 +189,7 @@ def test_most_corr_info_works(self): assert most_corr['feats'] == [] def test_most_corr_info_works_with_options(self): - geoms = md.load(self.MD_trajectory, top=self.MD_topology) - most_corr = _bmutils.most_corr(self.tica, geoms=geoms) + most_corr = _bmutils.most_corr(self.tica, geoms=self.MD_trajectories[0]) # Idxs are okay ref_idxs = [np.abs(self.tica.feature_TIC_correlation[:, ii]).argmax() for ii in range(self.tica.dim)] @@ -168,15 +200,13 @@ def test_most_corr_info_works_with_options(self): assert np.all([rv == mcv for rv, mcv in zip(ref_corrs, most_corr['vals'])]) # Check that we got the right most correlated feature trajectory - ref_feats = self.feat.transform(geoms) + ref_feats = self.feat.transform(self.MD_trajectories[0]) ref_feats = [ref_feats[:, ii] for ii in ref_idxs] assert np.all(np.allclose(rv, mcv) for rv, mcv in zip(ref_feats, np.squeeze(most_corr['feats']))) def test_most_corr_info_works_with_options_and_proj_idxs(self): - geoms = md.load(self.MD_trajectory, top=self.MD_topology) - proj_idxs = [1, 0] # the order shouldn't matter - corr_dict = _bmutils.most_corr(self.tica, geoms=geoms, proj_idxs=proj_idxs) + corr_dict = _bmutils.most_corr(self.tica, geoms=self.MD_trajectories[0], proj_idxs=proj_idxs) # Idxs are okay ref_idxs = [np.abs(self.tica.feature_TIC_correlation[:, ii]).argmax() for ii in proj_idxs] @@ -192,17 +222,13 @@ def test_most_corr_info_works_with_options_and_proj_idxs(self): assert [isinstance(istr, str) for istr in corr_dict["labels"]] # Feature values are ok - ref_feats = self.feat.transform(geoms) + ref_feats = self.feat.transform(self.MD_trajectories[0]) ref_feats = [ref_feats[:, ii] for ii in ref_idxs] assert np.all(np.allclose(rv, mcv) for rv, mcv in zip(ref_feats, np.squeeze(corr_dict['feats']))) def test_most_corr_info_wrong_proj_idxs(self): - proj_idxs = [1, 0, 10] # we don't have 10 TICs - try: - _bmutils.most_corr(self.tica, proj_idxs=proj_idxs) - except(ValueError): - pass #this should given this type of error + assert_raises(ValueError, _bmutils.most_corr, self.tica, proj_idxs=proj_idxs) class TestClusteringAndCatalogues(unittest.TestCase): @@ -225,12 +251,57 @@ def setUp(self): def test_cluster_to_target(self): n_target = 15 - data = [np.random.randn(100, 1), np.random.randn(100,1)+10] - cl = _bmutils.regspace_cluster_to_target(data, n_target, n_try_max=10, delta=0, verbose=True) - assert n_target - 1 <= cl.n_clusters <= n_target + 1 + n_tol = 1 + data = [np.random.randn(5000, 1), np.random.randn(5000,1)+10] + + # Only one iteration, just to force to go into the loop-breaking + cl = _bmutils.regspace_cluster_to_target_kmeans(data, n_target, k_centers=100, max_iter=1, n_tol=n_tol) + + # Now the real deal + cl = _bmutils.regspace_cluster_to_target_kmeans(data, n_target, k_centers=100, max_iter=100, n_tol=n_tol) + assert n_target - n_tol <= cl.n_clusters <= n_target + n_tol, (cl.n_clusters, n_tol) + + def test_regspace_from_distance_matrix(self): + data = np.random.rand(100, 2) + D = _squareform(_pdist(data)) + + centers = _bmutils.regspace_from_distance_matrix(D, D.mean()) + + # Re-compute distances, only for the centers + Drs = _pdist(data[centers]) + assert Drs.min()>=D.mean(), (Drs.min(), D.mean()) + + def test_interval_schachtelung(self): + + y = lambda x:x**2 # parabolic curve, monotically increasing between [0, +inf] + + interval = [2, 500] + eps = .1 + + target_y = np.random.randint(np.ceil(y(interval[0])), + np.floor(y(interval[1])), size=1).squeeze() + + x_sol = _bmutils.interval_schachtelung(y, [2, 500], target=target_y, eps=eps, + #verbose=True + ) + assert y(x_sol)-eps <= target_y < y(x_sol)+eps, (y(x_sol)-target_y, eps) + + def test_interval_schachtelung_fails(self): + y = lambda x:x**2 # parabolic curve, monotically increasing between [0, +inf] + + interval = [2, 500] + eps = .1 + + target_y = 0 # Is not contained in [2**2, 500**2] + + + assert_raises(Exception, _bmutils.interval_schachtelung, y, [2, 500], target=target_y, eps=eps, + #verbose=True + ) + def test_catalogues(self): - cl = _bmutils.regspace_cluster_to_target(self.data_for_cluster, 3, n_try_max=10, delta=0) + cl = _bmutils.regspace_cluster_to_target_kmeans(self.data_for_cluster, 3, max_iter=10, n_tol=0) #print(cl.clustercenters) cat_idxs, cat_cont = _bmutils.catalogues(cl) @@ -264,7 +335,7 @@ def test_catalogues(self): [13, 2]]) def test_catalogues_with_data(self): - cl = _bmutils.regspace_cluster_to_target(self.data_for_cluster, 3, n_try_max=10, delta=0) + cl = _bmutils.regspace_cluster_to_target_kmeans(self.data_for_cluster, 3, max_iter=10, n_tol=0) #print(cl.clustercenters) cat_idxs, cat_cont = _bmutils.catalogues(cl, data=self.data_for_cluster) @@ -299,7 +370,7 @@ def test_catalogues_with_data(self): def test_catalogues_sort_by_zero(self): - cl = _bmutils.regspace_cluster_to_target(self.data_for_cluster, 3, n_try_max=10, delta=0) + cl = _bmutils.regspace_cluster_to_target_kmeans(self.data_for_cluster, 3, max_iter=10, n_tol=0) cat_idxs, cat_cont = _bmutils.catalogues(cl, sort_by=0) # This test is extra, since this is a pure pyemma function @@ -328,7 +399,7 @@ def test_catalogues_sort_by_zero(self): [13, 2]]) def test_catalogues_sort_by_other_than_zero(self): - cl = _bmutils.regspace_cluster_to_target(self.data_for_cluster, 3, n_try_max=10, delta=0) + cl = _bmutils.regspace_cluster_to_target_kmeans(self.data_for_cluster, 3, max_iter=10, n_tol=0) cat_idxs, cat_cont = _bmutils.catalogues(cl, sort_by=1) # This test is extra, since this is a pure pyemma functions assert np.allclose(cat_idxs[0], [[1,0]]) @@ -390,7 +461,7 @@ class TestGetGoodStartingPoint(unittest.TestCase): def setUp(self): # The setup creates the typical, "geometries-sampled along cluster-scenario" n_geom_samples = 20 - traj = md.load(os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_ca.pdb')) + traj = md.load(molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb')) traj = traj.atom_slice([0,1,3,4]) # create a trajectory with four atoms # Create a fake bi-modal trajectory with a compact and an open structure ixyz = np.array([[0., 0., 0.], @@ -406,17 +477,14 @@ def setUp(self): z = np.random.permutation(z) coords[:, -1, -1] = z self.traj = md.Trajectory(coords, traj.top) - self.cl = _bmutils.regspace_cluster_to_target(self.traj.xyz[:, -1, -1], 50, n_try_max=10) + self.cl = _bmutils.regspace_cluster_to_target_kmeans(self.traj.xyz[:, -1, -1], 50, max_iter=10) self.cat_smpl = self.cl.sample_indexes_by_cluster(np.arange(self.cl.n_clusters), n_geom_samples) self.geom_smpl = self.traj[np.vstack(self.cat_smpl)[:,1]] self.geom_smpl = _bmutils.re_warp(self.geom_smpl, [n_geom_samples] * self.cl.n_clusters) def test_throws_exception(self): - try: - start_idx = _bmutils.get_good_starting_point(self.cl, self.geom_smpl, strategy="what?") - except NotImplementedError: - pass + assert_raises(NotImplementedError, _bmutils.get_good_starting_point, self.cl, self.geom_smpl, strategy="what?") # This test doesn't exactly belong here but this is the best class for now def test_find_centers_GMM(self): @@ -446,7 +514,7 @@ def test_most_pop(self): "around the value 15. The found starting point should be" \ "in this interval (see the setUp)" - def _test_most_pop_x_rgyr(self): + def test_most_pop_x_rgyr(self): start_idx = _bmutils.get_good_starting_point(self.cl, self.geom_smpl, strategy="most_pop_x_smallest_Rgyr") #print(start_idx, self.cl.clustercenters[start_idx], np.sort(self.cl.clustercenters.squeeze())) # TODO: figure out a good way of testing this, at the moment it just chekcs that it runs @@ -493,8 +561,7 @@ class TestVisualPath(TestWithBPTIData): def setUpClass(self): TestWithBPTIData.setUpClass() n_sample = 20 - self.cl_cont = _bmutils.regspace_cluster_to_target([ixyz[:, :2] for ixyz in self.xyz_flat], n_sample, verbose=True, n_try_max=10, - #delta=0 + self.cl_cont = _bmutils.regspace_cluster_to_target_kmeans([ixyz[:, :2] for ixyz in self.xyz_flat], n_sample, verbose=True, max_iter=10, ) self.cat_idxs, self.cat_data = _bmutils.catalogues(self.cl_cont) # Create the MD catalogue with pyemma @@ -512,14 +579,8 @@ def test_input_parsing(self): _bmutils.visual_path(self.cat_idxs, self.cat_data, start_pos=1) # Not implemented Errors - try: - _bmutils.visual_path(self.cat_idxs, self.cat_data, start_pos="other") - except NotImplementedError: - pass - try: - _bmutils.visual_path(self.cat_idxs, self.cat_data, path_type="xxxx") - except NotImplementedError: - pass + assert_raises(NotImplementedError, _bmutils.visual_path, self.cat_idxs, self.cat_data, start_pos="other") + assert_raises(NotImplementedError, _bmutils.visual_path, self.cat_idxs, self.cat_data, path_type="xxxx") class TestMinDispPaths(unittest.TestCase): @@ -567,11 +628,10 @@ def test_closest_all_coords_history(self): class TestSliceListOfGeoms(unittest.TestCase): def setUp(self): - self.topology = os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_ca.pdb') + self.topology = molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb') self.ref_frame = 4 - self.MD_trajectory = md.load(os.path.join(pyemma.__path__[0], - 'coordinates/tests/data/bpti_mini.xtc'), - top=self.topology) + self.MD_trajectory = md.load(glob(molpx._molpxdir(join='notebooks/data/c-alpha_centered.stride.1000*xtc'))[0], + top=self.topology) def test_slice(self): geom_list = [self.MD_trajectory, self.MD_trajectory[::-1]] @@ -591,24 +651,21 @@ def setUp(self): def test_it_works(self): assert np.allclose([0], _bmutils.get_ascending_coord_idx(self.data[:,:-1])) def test_empty_no_fail(self): - result = _bmutils.get_ascending_coord_idx(self.data[:,[1,2]], fail_if_empty=False) assert len(result)==0 def test_empty_fail(self): - try: - _bmutils.get_ascending_coord_idx(self.data[:, 1:], fail_if_empty=True) - except ValueError: - pass + assert_raises(ValueError, _bmutils.get_ascending_coord_idx, self.data[:, [1,2]], fail_if_empty=True) + def test_more_than_one_fails(self): - try: - _bmutils.get_ascending_coord_idx(self.data[:, :], fail_if_more_than_one=True) - except: - pass + assert_raises(Exception, _bmutils.get_ascending_coord_idx, self.data[:, :], fail_if_more_than_one=True) + + def test_more_than_one_passes(self): + _bmutils.get_ascending_coord_idx(self.data[:, :], fail_if_more_than_one=False) class TestMinRmsdPaths(unittest.TestCase): def setUp(self): - self.topology = os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_ca.pdb') + self.topology = molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb') self.reftraj = md.load(self.topology) def test_find_buried_best_candidate(self): @@ -623,7 +680,11 @@ def test_find_buried_best_candidate(self): for pp, ff in enumerate(frames_where_actual_geometry_is): xyz[pp][ff,:,:] = self.reftraj.xyz # Create the path of candidates as mdtraj objects - path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top) for ixyz in xyz] + path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top, + time=[self.reftraj.time.squeeze()] * n_cands, + unitcell_angles=[self.reftraj.unitcell_angles.squeeze()] * n_cands, + unitcell_lengths=[self.reftraj.unitcell_lengths.squeeze()] * n_cands) + for ixyz in xyz] # Let mirmsd_path find these frames for you inferred_frames = _bmutils.min_rmsd_path(self.reftraj, path_of_candidates) @@ -664,7 +725,11 @@ def test_find_buried_best_candidate_with_selection(self): xyz[pp][ff,:,:] = ref_w_sel_perturbed[pp] # Create the path of candidates as mdtraj objects - path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top) for ixyz in xyz] + path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top, + time=[self.reftraj.time.squeeze()] * n_cands, + unitcell_angles=[self.reftraj.unitcell_angles.squeeze()] * n_cands, + unitcell_lengths=[self.reftraj.unitcell_lengths.squeeze()] * n_cands) + for ixyz in xyz] # PRE-TEST # as long as the perturbation is small, @@ -689,7 +754,11 @@ def test_find_buried_best_candidate_with_selection(self): # In the random frames, insert the intouched selection for pp, ff in enumerate(frames_random_w_sel_untouched): xyz[pp][ff,selection,:] = np.copy(self.reftraj.xyz[0,selection, :]) - path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top) for ixyz in xyz] + path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top, + time=[self.reftraj.time.squeeze()] * n_cands, + unitcell_angles=[self.reftraj.unitcell_angles.squeeze()] * n_cands, + unitcell_lengths=[self.reftraj.unitcell_lengths.squeeze()] * n_cands) + for ixyz in xyz] # This should still be OK, because # even if the selected atoms have been perturbed, the comparsion [ref+sel_per] vs [random] is robust @@ -716,8 +785,12 @@ def test_history_aware_just_works(self): frames_where_actual_geometry_is = np.random.randint(0, high=n_cands, size=path_length) for pp, ff in enumerate(frames_where_actual_geometry_is): xyz[pp][ff, :, :] = self.reftraj.xyz - # Create the path of candidates as mdtraj objects - path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top) for ixyz in xyz] + # Create the path of candidates as mdtraj objects (time, unitcell angles and lengths are bogus) + path_of_candidates = [md.Trajectory(ixyz, topology=self.reftraj.top, + time=[self.reftraj.time.squeeze()]*n_cands, + unitcell_angles=[self.reftraj.unitcell_angles.squeeze()]*n_cands, + unitcell_lengths=[self.reftraj.unitcell_lengths.squeeze()]*n_cands) + for ixyz in xyz] # Let mirmsd_path find these frames for you inferred_frames = _bmutils.min_rmsd_path(self.reftraj, path_of_candidates, history_aware=True) @@ -728,7 +801,7 @@ class TestSmoothingFunctions(unittest.TestCase): def setUp(self): # The setup creates the typical, "geometries-sampled along cluster-scenario" - traj = md.load(os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb')) + traj = md.load(molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb')) traj = traj.atom_slice([0, 1]) # create a trajectory with two atoms # Create a fake bi-modal trajectory with a compact and an open structure ixyz = np.array([[10., 20., 30.], @@ -741,7 +814,7 @@ def tearDown(self): pass def test_running_avg_idxs_none(self): - idxs, windows = _bmutils.running_avg_idxs(10, 0) + idxs, windows = _bmutils.running_avg_idxs(10, 0, debug=True) # If the running average is with radius zero, it's just a normal average assert np.allclose([0,1,2,3,4,5,6,7,8,9], idxs) assert np.all(np.allclose(ii,jj) for ii, jj in zip(([0,1,2,3,4,5,6,7,8,9], windows))) @@ -771,14 +844,12 @@ def test_running_avg_idxs_two(self): windows)) def test_running_avg_idxs_too_large_window(self): - try: - idxs, windows = _bmutils.running_avg_idxs(10, 5) - except AssertionError: - pass + assert_raises(AssertionError, _bmutils.running_avg_idxs, 10, 5) + def test_raises_if_not_symmetric(self): + assert_raises(NotImplementedError, _bmutils.running_avg_idxs, 10, 5, symmetric=False) def test_smooth_geom_it_just_runs_and_gives_correct_output_type(self): - # No data assert isinstance(_bmutils.smooth_geom(self.traj, 0), md.Trajectory) assert isinstance(_bmutils.smooth_geom(self.traj, 0, superpose=False), md.Trajectory) @@ -823,7 +894,7 @@ class TestListTransposeGeomList(unittest.TestCase): def test_it(self): # Create a dummy topology - traj = md.load(os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb')) + traj = md.load(molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb')) top = traj.atom_slice([0]).top # Single atom topology ixyz_row_0 = [[0, 0, 0]], [[0, 1, 0]], [[0, 2, 0]] # 3 frames of 1 atom @@ -849,7 +920,7 @@ class geom_list_2_geom(unittest.TestCase): def test_it(self): # Create a dummy topology MD_trajectory = os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_mini.xtc') - MD_topology = os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb') + MD_topology = molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb') traj = md.load(MD_trajectory, top=MD_topology) traj_list = [itraj for itraj in traj] @@ -857,53 +928,146 @@ def test_it(self): assert np.allclose(np.hstack([igeom.xyz for igeom in new_geom]).squeeze(), np.vstack(traj.xyz)) -class TestIndexFromFeatures(unittest.TestCase): +class TestAtomIndices(unittest.TestCase): def setUp(self): - self.MD_topology = os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb') - self.feat= pyemma.coordinates.featurizer(self.MD_topology) + self.MD_topology = molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb') + self.feat = pyemma.coordinates.featurizer(self.MD_topology) - self.ang_idxs = [[ii + jj for jj in range(3)] for ii in range(self.feat.topology.n_atoms - 3)] - self.dih_idxs = [[ii + jj for jj in range(4)] for ii in range(self.feat.topology.n_atoms - 4)] + self.cartesian_input = np.arange(10) + self.distance_input = np.arange(10, 15) + self.angle_input = [[ii + jj for jj in range(3)] for ii in range(self.feat.topology.n_atoms - 3)] + self.dih_input = [[ii + jj for jj in range(4)] for ii in range(self.feat.topology.n_atoms - 4)] + self.mindist_input = [[15, 16], [16, 17], [18, 19]] - self.feat.add_all() - self.feat.add_distances_ca(excluded_neighbors=0) - self.feat.add_angles(self.ang_idxs) - self.feat.add_angles(self.ang_idxs, cossin=True) - self.feat.add_dihedrals(self.dih_idxs) - self.feat.add_dihedrals(self.dih_idxs, cossin=True) + # Add features to the featurizer and keep track of the atom indices + self.feat.add_selection(self.cartesian_input) + self.cartesian_indices = list(np.repeat(self.cartesian_input, 3)) + + self.feat.add_distances(self.distance_input) + self.distance_indices = [] + for ii, iidx in enumerate(self.distance_input[:-1]): + for jidx in self.distance_input[ii + 1:]: + self.distance_indices.append((iidx, jidx)) + + self.feat.add_angles(self.angle_input) + self.angle_indices = self.angle_input + + self.feat.add_angles(self.angle_input, cossin=True) + self.angle_indices_cossin = list(np.tile(self.angle_input, 2).reshape(-1, 3)) + + self.feat.add_dihedrals(self.dih_input) + self.dih_indices = self.dih_input + + self.feat.add_dihedrals(self.dih_input, cossin=True) + self.dih_indices_cossin = list(np.tile(self.dih_input, 2).reshape(-1, 4)) + + self.feat.add_residue_mindist(self.mindist_input) + self.mindist_indices = [[_bmutils.get_repr_atom_for_residue(self.feat.topology.residue(ii)).index for ii in rpair] for rpair in self.mindist_input] + + # Now put all indices in one long list: + self.ref = self.cartesian_indices+\ + self.distance_indices+\ + self.angle_indices+\ + self.angle_indices_cossin+\ + self.dih_indices+\ + self.dih_indices_cossin+\ + self.mindist_indices + + self.src = pyemma.coordinates.source(glob(molpx._molpxdir(join='notebooks/data/c-alpha*xtc'))[0], features=self.feat) + self.tica = pyemma.coordinates.tica(self.src ) + self.pca = pyemma.coordinates.pca(self.src) def tearDown(self): pass + def test_put_atom_idxs_on_widget(self): + import nglview + iwd = nglview.show_file(self.MD_topology) + #Regular input + _bmutils.add_atom_idxs_widget(self.ref, iwd) + # Insuficient colors + _bmutils.add_atom_idxs_widget(self.ref, iwd, ["red"]) + # Too many indices raises + assert_raises(NotImplementedError, _bmutils.add_atom_idxs_widget, [[1,2,3,4,5]], iwd) + + def test_repr_atoms_from_residues(self): + # Use Di-Ala + top = md.load(molpx._molpxdir(join='notebooks/data/ala2.pdb')).top + # First res, ACE + aa = _bmutils.get_repr_atom_for_residue(top.residue(0)) + assert aa.name=="C" + # Mid res, ala2 + aa = _bmutils.get_repr_atom_for_residue(top.residue(1)) + assert aa.name=="CA" + # Last res, ACE + aa = _bmutils.get_repr_atom_for_residue(top.residue(2)) + assert aa.name == "C" + + # Force fail + assert_raises(ValueError, _bmutils.get_repr_atom_for_residue, top.residue(0), cands=["CB"]) + + # Feature by feature + def test_atom_idxs_from_feature_not_recognized(self): + assert_raises(NotImplementedError, _bmutils.atom_idxs_from_feature, "aa") + def test_atom_idxs_from_feature_xyz(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[0]) - assert np.allclose(np.repeat(np.arange(self.feat.topology.n_atoms),3), ai) + assert np.allclose(self.cartesian_indices, ai) - def test_atom_idxs_from_feature_D_CA(self): + def test_atom_idxs_from_feature_D(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[1]) - ref = np.vstack(np.triu_indices(self.feat.topology.n_atoms, k=1)).T - assert np.allclose(ref, ai),ai + assert np.allclose(self.distance_indices, ai),ai def test_atom_idxs_from_feature_ang(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[2]) - assert np.allclose(self.ang_idxs, ai) + assert np.allclose(self.angle_indices, ai) def test_atom_idxs_from_feature_ang_cossin(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[3]) - ref = np.tile(self.ang_idxs, 2).reshape(-1,3) - assert np.allclose(ai, ref) + assert np.allclose(ai, self.angle_indices_cossin) def test_atom_idxs_from_feature_dih(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[4]) - assert np.allclose(self.dih_idxs, ai) + assert np.allclose(self.dih_indices, ai) + def test_atom_idxs_from_feature_dih_cossin(self): ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[5]) - ref = np.tile(self.dih_idxs, 2).reshape(-1,4) - assert np.allclose(ref, ai) + assert np.allclose(self.dih_indices_cossin, ai) + + def test_atom_idxs_from_feature_res_mindist(self): + ai = _bmutils.atom_idxs_from_feature(self.feat.active_features[6]) + assert np.allclose(self.mindist_indices,ai) + + def test_all_features_together(self): + ai = _bmutils.atom_idxs_from_general_input(self.feat) + + assert len(ai)==self.feat.dimension() + assert all([np.allclose(aa, rr) for aa, rr in zip(ai, self.ref)]) + + # Test the general input + def test_general_input_just_runs(self): + _bmutils.atom_idxs_from_general_input(self.feat) + _bmutils.atom_idxs_from_general_input(self.tica) + _bmutils.atom_idxs_from_general_input(self.pca) + assert_raises(TypeError, _bmutils.atom_idxs_from_general_input, "aa") + + def test_general_input_produces_the_right_indices(self): + ai = _bmutils.atom_idxs_from_general_input(self.feat) + assert all([np.allclose(aa, rr) for aa, rr in zip(ai, self.ref)]) + ai = _bmutils.atom_idxs_from_general_input(self.tica) + assert all([np.allclose(aa, rr) for aa, rr in zip(ai, self.ref)]) + ai = _bmutils.atom_idxs_from_general_input(self.pca) + assert all([np.allclose(aa, rr) for aa, rr in zip(ai, self.ref)]) + +def test_colors(): + _bmutils.matplotlib_colors_no_blue() class TestInputManipulationShaping(unittest.TestCase): + def test_listify_if_int(self): + assert isinstance(_bmutils.listify_if_int(0), list) + def test_listify(self): input = [1,2,3,4] output = _bmutils.listify_if_not_list(input) @@ -942,8 +1106,11 @@ def test_labelize(self): assert labels[0] == feat.describe()[0] assert labels[1] == feat.describe()[1] + # Raises Error + assert_raises(TypeError, _bmutils.labelize,1, [0,1]) + def test_superpose_list_of_geoms(self): - geom = md.load(os.path.join(pyemma.__path__[0], 'coordinates/tests/data/bpti_ca.pdb')) + geom = md.load(molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb')) # Nothing happens _bmutils.superpose_to_most_compact_in_list(False, [geom]) diff --git a/molpx/tests/test_generate.py b/molpx/tests/test_generate.py new file mode 100644 index 0000000..5313bc2 --- /dev/null +++ b/molpx/tests/test_generate.py @@ -0,0 +1,80 @@ +__author__ = 'gph82' + +import unittest +import pyemma +import numpy as np +import molpx +from matplotlib import pyplot as plt +from .test_bmutils import TestWithBPTIData + + +class TestSample(TestWithBPTIData): + + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + + def test_just_runs_input_file_one(self): + molpx.generate.sample(self.MD_trajectory_files[0], self.MD_topology_file, self.projected_files_npy[0]) + + def test_just_runs_input_file_many(self): + molpx.generate.sample(self.MD_trajectory_files, self.MD_topology_file, self.projected_files_npy) + + def test_just_runs_input_objects(self): + molpx.generate.sample(self.MD_trajectories, self.MD_topology, self.Ys) + + def test_gen_n_samples(self): + molpx.generate.sample(self.MD_trajectories, self.MD_topology, self.Ys, n_geom_samples=5) + + def test_gen_and_keep_n_samples(self): + molpx.generate.sample(self.MD_trajectories, self.MD_topology, self.Ys, n_geom_samples=5, keep_all_samples=True) + + def test_atom_selections(self): + __, geom_smpl = molpx.generate.sample(self.MD_trajectories, self.MD_topology, self.Ys, atom_selection=np.array([2,4,6,8])) + assert geom_smpl.n_atoms == 4 + + def test_use_cl_as_input(self): + cl = pyemma.coordinates.cluster_kmeans(self.Ys, 10) + molpx.generate.sample(self.MD_trajectories, self.MD_topology, cl) + + def test_right_data_are_returned(self): + # The only way to test this easily is inputting the clusterobject oneself + cl = pyemma.coordinates.cluster_kmeans(self.Ys, 10) + pos, geom_sampl = molpx.generate.sample(self.MD_trajectories, self.MD_topology, cl) + output_assignments = cl.assign(self.tica.transform(self.feat.transform(geom_sampl))[:,:2]) + for ii, oa in enumerate(output_assignments): + # Each frame of the output geometries must've been assigned to each clustercenter + assert ii == oa + # The output sample corresponds to that of the input (in projected space) + assert np.allclose(cl.clustercenters[ii], pos[oa]) + + def test_return_data(self): + molpx.generate.sample(self.MD_trajectories, self.MD_topology, self.Ys, return_data=True) + +class TestProjectionPath(TestWithBPTIData): + + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + + def test_just_runs(self): + molpx.generate.projection_paths(self.MD_trajectories, self.MD_topology, self.Ys) + + def test_just_runs_one_proj_idx(self): + molpx.generate.projection_paths(self.MD_trajectories, self.MD_topology, self.Ys, proj_idxs=1) + + def _test_right_geoms_are_returned(self): + #Each individual method of generate.projection_path has already been tested. + # TODO tomorrow, use ideas from TestMinRmsdPaths + pass + +if __name__ == '__main__': + unittest.main() diff --git a/molpx/tests/test_linkutils.py b/molpx/tests/test_linkutils.py index 2d4e310..6f48aed 100644 --- a/molpx/tests/test_linkutils.py +++ b/molpx/tests/test_linkutils.py @@ -7,15 +7,13 @@ from glob import glob from matplotlib.backend_bases import MouseEvent import mdtraj as md -import matplotlib.pyplot as plt #plt.switch_backend('Agg') # allow tests import nglview +from numpy.testing import assert_raises from scipy.spatial import cKDTree as _cKDTree - - class TestLinkAxWPos2NGLWidget(unittest.TestCase): @classmethod @@ -31,41 +29,38 @@ def setUpClass(self): self.pos[:,1] = np.random.randn(self.n_sample) self.geom = self.MD_trajectories[0][:20] self.ngl_wdg = nglview.show_mdtraj(self.geom) + import matplotlib.pyplot as plt + self.plt = plt def test_just_works(self): - plt.figure() - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), self.pos, self.ngl_wdg) + self.plt.figure() + __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(self.plt.gca(), self.pos, self.ngl_wdg) def test_just_works_bandwidth(self): - plt.figure() - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), self.pos, self.ngl_wdg, + self.plt.figure() + __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(self.plt.gca(), self.pos, self.ngl_wdg, band_width=[0.1, .1]) def test_just_works_exclude_coord(self): - plt.figure() - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), self.pos, self.ngl_wdg, + self.plt.figure() + __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(self.plt.gca(), self.pos, self.ngl_wdg, exclude_coord=1) def test_force_exceptions(self): - plt.figure() - try: - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), self.pos, self.ngl_wdg, + self.plt.figure() + assert_raises(TypeError, molpx._linkutils.link_ax_w_pos_2_nglwidget, self.plt.gca(), self.pos, self.ngl_wdg, dot_color=2) - except TypeError: - pass - plt.figure() - try: - pos_rnd = np.random.randn(self.n_sample, 2) - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), pos_rnd, self.ngl_wdg, + self.plt.figure() + pos_rnd = np.random.randn(self.n_sample, 2) + assert_raises(ValueError, molpx._linkutils.link_ax_w_pos_2_nglwidget, self.plt.gca(), pos_rnd, self.ngl_wdg, band_width=[.1, .1]) - except ValueError: - pass def test_just_works_radius(self): - plt.figure() - __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(plt.gca(), self.pos, self.ngl_wdg, + self.plt.figure() + __ = molpx._linkutils.link_ax_w_pos_2_nglwidget(self.plt.gca(), self.pos, self.ngl_wdg, band_width=[.1, .1], radius=True) + class TestGeometryInNGLWidget(unittest.TestCase): # TODO abstact this to a test class @@ -130,9 +125,91 @@ def test_show_and_hide_just_runs_and_changes_quickhands(self): # Now we hide many times, should arrive at the end without anything happening [giw.hide() for ii in range(10)] +class TestContactInNGLWidget(unittest.TestCase): + + # TODO abstact this to a test class + @classmethod + def setUpClass(self): + self.MD_trajectory_files = glob(molpx._molpxdir(join='notebooks/data/c-alpha_centered.stride.1000*xtc')) + self.MD_topology_file = molpx._molpxdir(join='notebooks/data/bpti-c-alpha_centered.pdb') + self.MD_topology = md.load(self.MD_topology_file).top + self.MD_trajectories = [md.load(ff, top=self.MD_topology_file) for ff in self.MD_trajectory_files] + + self.n_sample = 20 + self.pos = np.zeros((self.n_sample,2)) + self.pos[:,0] = np.linspace(0,1,self.n_sample) + self.pos[:,1] = np.random.randn(self.n_sample) + self.geom = self.MD_trajectories[0][:20] + + def test_just_works(self): + self.ngl_wdg = nglview.show_mdtraj(self.geom) + molpx._linkutils.ContactInNGLWidget(self.ngl_wdg, [0, 1], 0) + + def _test_right_atoms_are_represented(self): + self.ngl_wdg = nglview.show_mdtraj(self.geom) + ctcNwid = molpx._linkutils.ContactInNGLWidget(self.ngl_wdg, [0, 1], 0) + + # TODO cannot be tested since the representation is yet to be shown! + # we have to find this out! + #irepr = [value for value in self.ngl_wdg._ngl_repr_dict["0"].items() if value["type"] == "distance"] + #assert len(irepr)==1 + #assert np.allclose(np.sort(irepr["params"]["atomPair"]), [0,1]) + + def test_method_show_and_hide(self): + self.ngl_wdg = nglview.show_mdtraj(self.geom) + self.ngl_wdg.display() + ctcInwid = molpx._linkutils.ContactInNGLWidget(self.ngl_wdg, [0, 1], 0, verbose=True) + ctcInwid.show() + ctcInwid.hide() # Hide isn't really doing anything, since nothing is really shown + # TODO find out how to force the presentation on nglview from terminal + + def _test_quickhands(self): + giw = molpx._linkutils.GeometryInNGLWidget(self.geom, self.ngl_wdg) + assert giw.is_empty() + assert giw.have_repr == [] + assert ~giw.all_reps_are_on() + assert giw.all_reps_are_off() + assert ~giw.any_rep_is_on() + assert ~giw.is_visible() + + + def _test_show_just_runs_and_changes_quickhands(self): + giw = molpx._linkutils.GeometryInNGLWidget(self.geom, self.ngl_wdg) + giw.show() + assert ~giw.is_empty() + assert len(giw.have_repr) == 1 + assert giw.all_reps_are_on() + assert ~giw.all_reps_are_off() + assert giw.any_rep_is_on() + assert giw.is_visible() + + def _test_show_and_hide_just_runs_and_changes_quickhands(self): + # We initialize with two frames, it's easy to test the ends + giw = molpx._linkutils.GeometryInNGLWidget(self.geom[:2], self.ngl_wdg) + giw.show() + giw.hide() + assert ~giw.is_empty() + assert len(giw.have_repr) == 1 #should still be one + assert ~giw.all_reps_are_on() # but it´s off + assert giw.all_reps_are_off() + assert ~giw.any_rep_is_on() + assert ~giw.is_visible() + assert giw.any_rep_is_off() + # Now we show again + giw.show() # Turn on the one we had already, does not change the have_repr + assert len(giw.have_repr)==1 + giw.show() + assert len(giw.have_repr)==2 + # Turn on a new one, which isn't there nothing should happen + [giw.show() for ii in range(10)] + # Now we hide many times, should arrive at the end without anything happening + [giw.hide() for ii in range(10)] + + class TestUpdate2DLines(unittest.TestCase): def test_all(self): + import matplotlib.pyplot as plt plt.plot(0,0) line = plt.gca().lines[0] for ii, attr in enumerate(['lineh', 'linev', 'dot']): @@ -140,18 +217,17 @@ def test_all(self): molpx._linkutils.update2Dlines(line, ii, ii) def test_force_exceptions(self): + import matplotlib.pyplot as plt plt.plot(0, 0) line = plt.gca().lines[0] + # This passes interactively in the console...? try: - molpx._linkutils.update2Dlines(line,0,0) - except AttributeError: + assert_raises(AttributeError, molpx._linkutils.update2Dlines, line,0,0) + except AssertionError: pass setattr(line, "whatisthis", "non_existing_line") - try: - molpx._linkutils.update2Dlines(line,0,0) - except TypeError: - pass + assert_raises(TypeError, molpx._linkutils.update2Dlines, line,0,0) class TestClickOnAxisListener(unittest.TestCase): @classmethod @@ -164,11 +240,13 @@ def setUpClass(self): self.pos = np.random.rand(self.MD_trajectory.n_frames, 2) self.kdtree = _cKDTree(self.pos) + import matplotlib.pyplot as plt + self.plt = plt def just_runs(self, ngl_wdg, button=None): # Create the linked objects - plt.plot(self.pos[:,0], self.pos[:,1]) - iax = plt.gca() + self.plt.plot(self.pos[:,0], self.pos[:,1]) + iax = self.plt.gca() # Prepare a mouse event in the middle of the plot x, y = np.array(iax.get_window_extent()).mean(0) @@ -184,7 +262,7 @@ def just_runs(self, ngl_wdg, button=None): [lineh], iax, self.pos, [dot] - )(MouseEvent(" ", plt.gcf().canvas, x,y, + )(MouseEvent(" ", self.plt.gcf().canvas, x,y, button=button, key=None, step=0, dblclick=False, guiEvent=None)) @@ -194,13 +272,13 @@ def test_just_runs(self): def test_just_runs_sticky(self): # Create the linked objects (for sticky case, better use _sample - ngl_wdg, __ = molpx.visualize.sample(self.pos, self.MD_trajectory, plt.gca(), sticky=True) + ngl_wdg, __ = molpx.visualize.sample(self.pos, self.MD_trajectory, self.plt.gca(), sticky=True) self.just_runs(ngl_wdg, button=1) [self.just_runs(ngl_wdg, button=2) for ii in range(5)] def test_just_runs_recomputes_kdtree(self): - plt.plot(self.pos[:, 0], self.pos[:, 1]) - iax = plt.gca() + self.plt.plot(self.pos[:, 0], self.pos[:, 1]) + iax = self.plt.gca() # Prepare a mouse event in the middle of the plot x, y = np.array(iax.get_window_extent()).mean(0) @@ -223,6 +301,81 @@ def test_just_runs_recomputes_kdtree(self): dblclick=False, guiEvent=None)) + # Change axis lims to trigger recomputation + old_xlim = iax.get_xlim() + new_xmin = old_xlim[0]+abs(np.diff(iax.get_xlim())*.10) + iax.set_xlim(new_xmin, old_xlim[1]) + # Recompute the position of the new click + x, y = np.array(iax.get_window_extent()).mean(0) + # Send an event + CLAL(MouseEvent(" ", CLAL.ax.figure.canvas, x, y, + button=1, key=None, step=0, + dblclick=False, + guiEvent=None)) + + def test_click_w_contacts(self): + # Plot contact map + ctcs, idxs = md.compute_contacts(self.MD_trajectory) + ctcs = md.geometry.squareform(ctcs, idxs) + self.plt.imshow(ctcs[0]) + iax = self.plt.gca() + + # Create matching "positions" array + nres = ctcs.shape[-1] + positions = np.vstack(np.unravel_index(range(nres ** 2), (nres, nres))).T + + # Create widget and monkey-patch its _CtcsInWid attribute + ngl_wdg = nglview.show_mdtraj(self.MD_trajectory) + ngl_wdg._CtcsInWid = [molpx._linkutils.ContactInNGLWidget(ngl_wdg, [0, 1], 0)] + + # Create the CLA object linking the wid and the axis via "positions" + CLAL = molpx._linkutils.ClickOnAxisListener(ngl_wdg, True, + [], + iax, positions, + [] + ) + + # Get the left, uppermost pixel of the image (matplotlib voodoo, + # see https://matplotlib.org/users/transforms_tutorial.html + x, y = iax.get_window_extent().x0, iax.get_window_extent().y1 + + # Before the click, this should just pass + CLAL.remove_last_contacts() + + # Now we instantiate a mouseclick on the first contact (0,1) + ME = MouseEvent(" ", CLAL.ax.figure.canvas, x, y, + button=1, key=None, step=0, + dblclick=False, + guiEvent=None) + + # Make sure the simulated click was actually on the canvas + assert CLAL.ax.get_window_extent().contains(ME.x, ME.y) + + # Send the mouseclick to the CLAL + CLAL(ME) + + # Assert that a rectangle was created + assert CLAL.list_of_rects[0] is not None, CLAL.list_of_rects + + # This should remove that rectangle and the contacts + CLAL.remove_last_contacts() + # Check that "remove" worked: + assert ~ngl_wdg._CtcsInWid[0].shown + + # Now click again one on left and one on right click: + ME = MouseEvent(" ", CLAL.ax.figure.canvas, x, y, + button=1, key=None, step=0, + dblclick=False, + guiEvent=None) + ME_right = MouseEvent(" ", CLAL.ax.figure.canvas, x, y, + button=2, key=None, step=0, + dblclick=False, + guiEvent=None) + CLAL(ME) + CLAL(ME_right) + + + class TestChangeInNGLWidgetListener(unittest.TestCase): @classmethod def setUpClass(self): @@ -233,8 +386,10 @@ def setUpClass(self): self.MD_trajectory = self.MD_trajectories[0] self.pos = np.random.rand(self.MD_trajectory.n_frames, 2) - plt.plot(self.pos[:,0], self.pos[:,1]) - iax = plt.gca() + from matplotlib import pyplot as plt + self.plt = plt + self.plt.plot(self.pos[:,0], self.pos[:,1]) + iax = self.plt.gca() # Prepare event self.lineh = iax.axhline(iax.get_ybound()[0]) setattr(self.lineh, 'whatisthis', 'lineh') @@ -248,5 +403,18 @@ def test_just_runs_past_last_frame(self): molpx._linkutils.ChangeInNGLWidgetListener(self.ngl_wdg, [self.lineh, self.dot], self.pos)({"new":self.pos.shape[0]+1, "old":1}) + def test_update_contact_map(self): + from matplotlib import pyplot as _plt + contact_map = md.geometry.squareform(*md.compute_contacts(self.MD_trajectories[0])) + _plt.figure() + iax = _plt.gca() + self.ngl_wdg._MatshowData = {"image" : iax.matshow(contact_map[0]), + "data" : contact_map} + CINL = molpx._linkutils.ChangeInNGLWidgetListener(self.ngl_wdg, [], None) + # Run trough all available contact maps + for ii in range(len(contact_map)): + CINL({"new":ii}) + + if __name__ == '__main__': unittest.main() diff --git a/molpx/tests/test_molPX.py b/molpx/tests/test_molPX.py deleted file mode 100644 index 27d081e..0000000 --- a/molpx/tests/test_molPX.py +++ /dev/null @@ -1,60 +0,0 @@ -__author__ = 'gph82' - -import unittest -import pyemma -import os -import tempfile -import numpy as np -import shutil -import molpx -from matplotlib import pyplot as plt -#plt.switch_backend('Agg') # allow tests - - -class MyTestCase(unittest.TestCase): - - def setUp(self): - self.MD_trajectory = os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_mini.xtc') - self.topology = os.path.join(pyemma.__path__[0],'coordinates/tests/data/bpti_ca.pdb') - self.tempdir = tempfile.mkdtemp('test_molpx') - self.projected_file = os.path.join(self.tempdir,'Y.npy') - feat = pyemma.coordinates.featurizer(self.topology) - feat.add_all() - source = pyemma.coordinates.source(self.MD_trajectory, features=feat) - self.tica = pyemma.coordinates.tica(source,lag=1, dim=2) - Y = self.tica.get_output()[0] - print(self.tempdir) - np.save(self.projected_file,Y) - - def tearDown(self): - shutil.rmtree(self.tempdir) - - def test_generate_paths(self): - molpx.generate.projection_paths(self.MD_trajectory, self.topology, self.projected_file) - - def test_generate_sample(self): - molpx.generate.sample(self.MD_trajectory, self.topology, self.projected_file) - - def test_generate_sample_atom_selections(self): - molpx.generate.sample(self.MD_trajectory, self.topology, self.projected_file, atom_selection='symbol != H') - - __, geom_smpl = molpx.generate.sample(self.MD_trajectory, self.topology, self.projected_file, atom_selection=np.array([2,4,6,8])) - assert geom_smpl[0].n_atoms == 4 - - # Cannot get the widget to run outside the notebook because it needs an interact bar - def test_visualize_qsample(self): - pos, geom_smpl = molpx.generate.sample(self.MD_trajectory, self.topology, self.projected_file) - plt.figure() - __ = molpx.visualize.sample(pos, geom_smpl, plt.gca()) - - def test_visualize_path_w_tica(self): - paths_dict, idata = molpx.generate.projection_paths(self.MD_trajectory, self.topology, self.projected_file) - plt.figure() - path_type = 'min_disp' - igeom = paths_dict[0][path_type]["geom"] - ipath = paths_dict[0][path_type]["proj"] - __ = molpx.visualize.sample(ipath, igeom, plt.gca(), projection=self.tica) - - -if __name__ == '__main__': - unittest.main() diff --git a/molpx/tests/test_visualize.py b/molpx/tests/test_visualize.py index 24400b6..80518ad 100644 --- a/molpx/tests/test_visualize.py +++ b/molpx/tests/test_visualize.py @@ -9,7 +9,8 @@ import mdtraj as md import matplotlib.pyplot as plt import nglview -#plt.switch_backend('Agg') # allow tests +from numpy.testing import assert_raises +import pyemma from .test_bmutils import TestWithBPTIData @@ -33,8 +34,8 @@ def test_simplest_inputs_memory_small_max_frames(self): visualize.traj(self.MD_trajectories, self.MD_topology, self.Ys, max_frames=3) def test_simplest_inputs_disk(self): - visualize.traj(self.MD_trajectory_files, self.MD_topology_file, self.projected_files) - visualize.traj(self.MD_trajectory_files, self.MD_topology_file, [ifile.replace('.npy', '.dat') for ifile in self.projected_files]) + visualize.traj(self.MD_trajectory_files, self.MD_topology_file, self.projected_files_npy) + visualize.traj(self.MD_trajectory_files, self.MD_topology_file, [ifile.replace('.npy', '.dat') for ifile in self.projected_files_npy]) def test_simplest_inputs_memory_and_proj(self): visualize.traj(self.MD_trajectories, self.MD_topology, self.Ys, projection=self.tica) @@ -85,6 +86,40 @@ def test_weights_on_biased_FES(self): visualize.FES(self.metad_trajectory_files, self.ala2_topology_file, self.metad_colvar_files, proj_idxs=[1,2], weights=weights) +class TestFES(TestWithBPTIData): + + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + + def test_just_works_min_input_disk(self): + molpx.visualize.FES(self.MD_trajectory_files, + self.MD_topology_file, + self.projected_files_npy) + + def test_just_works_min_input_memory(self): + molpx.visualize.FES(self.MD_trajectories, + self.MD_topology, + self.Ys) + + def test_overlays(self): + molpx.visualize.FES(self.MD_trajectories, + self.MD_topology, + self.Ys, + n_overlays=5) + + def test_1D(self): + molpx.visualize.FES(self.MD_trajectories, + self.MD_topology, + self.Ys, + proj_idxs=[0]) + + def test_with_weigths(self): + weights = [np.ones(len(iY)) for iY in self.Ys] + molpx.visualize.FES(self.MD_trajectories, + self.MD_topology, + self.Ys, weights=weights) + class TestNGLWidgetWrapper(unittest.TestCase): def setUp(self): @@ -103,7 +138,6 @@ def test_widget_wrapper_w_instantiated_wdg(self): def test_colors(): _bmutils.matplotlib_colors_no_blue() - class TestSample(TestWithBPTIData): @classmethod @@ -115,6 +149,10 @@ def setUpClass(self): self.pos[:,1] = np.random.rand(self.n_sample) self.geom = self.MD_trajectories[0][:self.n_sample] + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + def test_sample_not_sticky_just_works(self): plt.figure() __ = molpx.visualize.sample(self.pos, self.geom, plt.gca()) @@ -170,34 +208,6 @@ def test_sample_sticky_just_works_list_geom_small_molecule(self): color_list=['r', 'b', 'g', 'magenta'], sticky=True) -class TestFES(TestWithBPTIData): - - @classmethod - def setUpClass(self): - TestWithBPTIData.setUpClass() - - def test_just_works_min_input_disk(self): - molpx.visualize.FES(self.MD_trajectory_files, - self.MD_topology_file, - self.projected_files) - - def test_just_works_min_input_memory(self): - molpx.visualize.FES(self.MD_trajectories, - self.MD_topology, - self.Ys) - - def test_overlays(self): - molpx.visualize.FES(self.MD_trajectories, - self.MD_topology, - self.Ys, - n_overlays=5) - - def test_1D(self): - molpx.visualize.FES(self.MD_trajectories, - self.MD_topology, - self.Ys, - proj_idxs=[0]) - class TestCorrelations(TestWithBPTIData): @classmethod @@ -222,6 +232,11 @@ def test_correlations_input_warn(self): def test_correlations_inputs_verbose(self): visualize.correlations(self.tica, verbose=True) + def test_correlations_inputs_verbose_and_widget(self): + visualize.correlations(self.tica, + geoms=self.MD_trajectories[0], + verbose=True) + def test_correlations_inputs_color_list_parsing(self): visualize.correlations(self.tica, proj_color_list=['green']) @@ -231,30 +246,120 @@ def test_correlations_inputs_FAIL_color_list_parsing(self): except TypeError: pass -class TestFeature(TestWithBPTIData): +class TestMSM(TestWithBPTIData): + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + self.cl = pyemma.coordinates.cluster_kmeans([iY[:, :2] for iY in self.Ys], 5) + self.iMSM = pyemma.msm.estimate_markov_model(self.cl.dtrajs, 1) + self.iHMM = self.iMSM.coarse_grain(3) + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + + def test_just_runs_MSM(self): + visualize.MSM(self.iMSM, self.MD_trajectories) + # More overlays + visualize.MSM(self.iMSM, self.MD_trajectories, n_overlays=10) + + def test_just_runs_HMM(self): + visualize.MSM(self.iHMM, self.MD_trajectories) + visualize.MSM(self.iHMM, self.MD_trajectories, n_overlays=10) + visualize.MSM(self.iHMM, self.MD_trajectories, sharpen=True) + + def test_just_runs_position_input(self): + visualize.MSM(self.iMSM, self.MD_trajectories, pos=self.cl.clustercenters) + visualize.MSM(self.iHMM, self.MD_trajectories, pos=self.cl.clustercenters) + + visualize.MSM(self.iHMM, self.MD_trajectories, pos=self.cl.clustercenters, sharpen=True) + visualize.MSM(self.iHMM, self.MD_trajectories, pos=self.cl.clustercenters[:self.iHMM.nstates]) + # test assert + assert_raises(TypeError, visualize.MSM, self.iMSM, self.MD_trajectories, pos=['a']) + assert_raises(AssertionError, visualize.MSM, self.iMSM, self.MD_trajectories, pos=self.cl.clustercenters[:-1]) + + def test_source_inputs(self): + visualize.MSM(self.iMSM, self.MD_trajectory_files, top=self.MD_topology_file) + visualize.MSM(self.iMSM, self.MD_trajectory_files, top=self.MD_topology) + visualize.MSM(self.iMSM, self.source) + + + def test_returns_the_right_things_MSM(self): + # Returning the right things should be guaranteed by all the + # lower-level methods, which are also unit-tested. Still, here we go + mpxbox = visualize.MSM(self.iMSM, self.MD_trajectories, n_overlays=3) + # We re-featurize, re-tic-transform, and re-cl-assign the output geoms + for igeoms in mpxbox.linked_mdgeoms: + out_assign = self.cl.assign(self.tica.transform(self.feat.transform(igeoms))[:,:2]) + assert np.allclose(out_assign, np.arange(self.cl.n_clusters)) + + # Now with sticky + mpxbox = visualize.MSM(self.iMSM, self.MD_trajectories, n_overlays=3, sticky=True) + # We re-featurize, re-tic-transform, and re-cl-assign the output geoms + for igeoms in mpxbox.linked_mdgeoms: + out_assign = self.cl.assign(self.tica.transform(self.feat.transform(igeoms))[:, :2]) + assert np.allclose(out_assign, np.arange(self.cl.n_clusters)) + + def test_returns_the_right_things_HMSM_sharpen(self): + # Returning the right things should be guaranteed by all the + # lower-level methods, which are also unit-tested. Still, here we go + mpxbox = visualize.MSM(self.iHMM, self.MD_trajectories, n_overlays=3, sharpen=True) + # We re-featurize, re-tic-transform, and re-cl-assign the output geoms + out_set = np.argmax(self.iHMM.observation_probabilities, axis=1) + for igeoms in mpxbox.linked_mdgeoms: + out_assign = self.cl.assign(self.tica.transform(self.feat.transform(igeoms))[:,:2]) + assert np.allclose(out_assign, out_set) + + +class TestFeature(TestWithBPTIData): @classmethod def setUpClass(self): TestWithBPTIData.setUpClass() + @classmethod + def tearDownClass(self): + TestWithBPTIData.tearDownClass() + def test_feature(self): plt.figure() iwd = nglview.show_mdtraj(self.MD_trajectories[0]) - visualize.feature(self.feat.active_features[0], iwd) + visualize.feature(self.feat, iwd) def test_feature_color_list(self): plt.figure() iwd = nglview.show_mdtraj(self.MD_trajectories[0]) - visualize.feature(self.feat.active_features[0], iwd, + visualize.feature(self.feat, iwd, idxs=[0,1], color_list=['blue']) try: - visualize.feature(self.feat.active_features[0], iwd, + visualize.feature(self.feat, iwd, idxs=[0,1], color_list='blue') except TypeError: pass +class Contacts(TestWithBPTIData): + @classmethod + def setUpClass(self): + TestWithBPTIData.setUpClass() + self.geom = self.MD_trajectories[0] + ctcs, res_idxs = md.compute_contacts(self.geom) + self.ctcs = md.geometry.squareform(ctcs, res_idxs) + + def test_just_runs(self): + visualize.contacts(self.ctcs, self.geom) + + def test_one_ctcframe(self): + # this should fail + try: + visualize.contacts(self.ctcs.mean(0), self.geom) + except AssertionError: + pass + # This should pass + visualize.contacts(self.ctcs.mean(0), self.geom, average=True) + + def test_raises(self): + assert_raises(NotImplementedError, visualize.contacts, self.ctcs.mean(0), self.geom, average=True, residue_indices=[1,2,3]) class TestBoxMe(unittest.TestCase): diff --git a/molpx/visualize.py b/molpx/visualize.py index cbff1c4..e93b7cd 100644 --- a/molpx/visualize.py +++ b/molpx/visualize.py @@ -77,6 +77,7 @@ def FES(MD_trajectories, MD_top, projected_trajectories, proj_labels='proj', n_overlays=1, atom_selection=None, + verbose=False, **sample_kwargs): r""" Return a molecular visualization widget connected with a free energy plot. @@ -132,9 +133,12 @@ def FES(MD_trajectories, MD_top, projected_trajectories, If :obj:`MD_trajectories` is already a (list of) :obj:`mdtraj.Trajectory` objects, the atom-slicing can be done by the user place before calling this method. + verbose : bool, default is False + Be verbose while computing the FES + sample_kwargs : dictionary of named arguments, optional - named arguments for the function :obj:`molpx.visualize.sample`. Non-expert users can safely ignore this option. Examples - are :obj:`superpose` or :obj:`proj_idxs` + named arguments for the function :obj:`molpx.visualize.sample`. Non-expert users can safely ignore this option. + Examples are :obj:`superpose` or :obj:`proj_idxs` Returns ------- @@ -148,13 +152,13 @@ def FES(MD_trajectories, MD_top, projected_trajectories, information being displayed. linked_axes : - list with all the :obj:`pylab.Axis`-objects contained in the :obj:`widgetbox` + list with all the :obj:`pyplot.Axis`-objects contained in the :obj:`widgetbox` linked_ax_wdgs : list with all the :obj:`matplotlib.widgets.AxesWidget`objects contained in the :obj:`widgetbox` linked_figs : - list with all the :obj:`pylab.Figure`-objects contained in the :obj:`widgetbox` + list with all the :obj:`pyplot.Figure`-objects contained in the :obj:`widgetbox` linked_ngl_wdgs : list with all the :obj:`nglview.NGLWidget`-objects contained in the :obj:`widgetbox` @@ -166,7 +170,7 @@ def FES(MD_trajectories, MD_top, projected_trajectories, list with all the :obj:`mdtraj.Trajectory`-objects contained in the :obj:`widgetbox` """ - from matplotlib import pylab as _plt + from matplotlib import pyplot as _plt # Prepare the overlay option n_overlays = _np.min([n_overlays,50]) if n_overlays>1: @@ -184,7 +188,8 @@ def FES(MD_trajectories, MD_top, projected_trajectories, return_data=True, n_geom_samples=n_overlays, keep_all_samples=keep_all_samples, - proj_stride=proj_stride + proj_stride=proj_stride, + verbose=verbose ) data = _np.vstack(data) @@ -220,8 +225,6 @@ def FES(MD_trajectories, MD_top, projected_trajectories, _linkutils.auto_append_these_mpx_attrs(outbox, geoms, ax, _plt.gcf(), ngl_wdg, axes_wdg, data_sample) return outbox - - def _box_me(tuple_in, auto_resize=True): r""" A wrapper that tries to put in an HBox whatever it s in @@ -238,7 +241,8 @@ def _box_me(tuple_in, auto_resize=True): :return: obj:IPywdigets.HBox:, if possible """ - + # TODO THIS IS UNUSED + # TODO EITHER USE IT OR REMOVE IT BEFORE RELEASING widgets_and_canvas = [] size_inches = [] for obj in tuple_in: @@ -275,7 +279,6 @@ def _box_me(tuple_in, auto_resize=True): return _linkutils._HBox(tuple_out) - def _plot_ND_FES(data, ax_labels, weights=None, bins=50, figsize=(4,4)): r""" A wrapper for pyemmas FESs plotting function that can also plot 1D @@ -289,14 +292,14 @@ def _plot_ND_FES(data, ax_labels, weights=None, bins=50, figsize=(4,4)): Returns ------- - ax : :obj:`pylab.Axis` object + ax : :obj:`pyplot.Axis` object FES_data : numpy nd.array containing the FES (only for 1D data) edges : tuple containimg the axes along which FES is to be plotted (only in the 1D case so far, else it's None) """ - from matplotlib import pylab as _plt + from matplotlib import pyplot as _plt _plt.figure(figsize=figsize) ax = _plt.gca() idata = _np.vstack(data) @@ -336,6 +339,7 @@ def traj(MD_trajectories, tunits = 'frames', traj_selection = None, projection = None, + input_feature_traj = None, n_feats = 1, ): r"""Link one or many :obj:`projected trajectories`, [Y_0(t), Y_1(t)...], with the :obj:`MD_trajectories` that @@ -346,7 +350,6 @@ def traj(MD_trajectories, MD_trajectories : str, or list of strings with the filename(s) the the molecular dynamics (MD) trajectories. Any file extension that :py:obj:`mdtraj` (.xtc, .dcd etc) can read is accepted. - Alternatively, a single :obj:`mdtraj.Trajectory` object or a list of them can be given as input. MD_top : str to topology filename or directly :obj:`mdtraj.Topology` object @@ -398,16 +401,20 @@ def traj(MD_trajectories, traj_selection : None, int, iterable of ints, default is None Don't plot all trajectories but only few of them. The default None implies that all trajs will be plotted. - Note: the data used for the FES will always include all trajectories, regardless of this value + Note: the data used for the FES will only include these trajectories projection : object that generated the projection, default is None The projected coordinates may come from a variety of sources. When working with :obj:`pyemma` a number of objects might have generated this projection, like a :obj:`pyemma.coordinates.transform.TICA` or a :obj:`pyemma.coordinates.transform.PCA` - Pass this object along and observe and the features that are most correlated with the projections + Pass this object along and observe the features that are most correlated with the projections will be plotted for the active trajectory, allowing the user to establish a visual connection between the projected coordinate and the original features (distances, angles, contacts etc) + These trajectories will be re-computed by applyiing + :obj:`projection.transform(MD_trajectories)', unless :obj:`input_feature_traj` is parsed + + input_feature_traj : TODO n_feats : int, default is 1 If a :obj:`projection` is passed along, the first n_feats features that most correlate the @@ -421,9 +428,9 @@ def traj(MD_trajectories, return _plt.gca(), _plt.gcf(), widget, geoms ax : - :obj:`pylab.Axis` object + :obj:`pyplot.Axis` object fig : - :obj:`pylab.Figure` object + :obj:`pyplot.Figure` object ngl_wdg : :obj:`nglview.NGLWidget` geoms: @@ -431,7 +438,7 @@ def traj(MD_trajectories, """ - from matplotlib import pylab as _plt + from matplotlib import pyplot as _plt smallfontsize = int(_rcParams['font.size'] / 1.5) proj_idxs = _bmutils.listify_if_int(proj_idxs) @@ -481,17 +488,18 @@ def traj(MD_trajectories, for proj_counter, __ in enumerate(proj_idxs): ylims[0, proj_counter] = _np.min([idata[:,proj_counter].min() for idata in data]) ylims[1, proj_counter] = _np.max([idata[:,proj_counter].max() for idata in data]) - + ylabels = _bmutils.labelize(proj_labels, proj_idxs) # Do we have usable projection information? - corr_dicts = [[]]*n_trajs + corr_dicts = [[]]*len(data) if projection is not None: corr_dicts = [_bmutils.most_corr(projection, geoms=igeom, proj_idxs=proj_idxs, n_args=n_feats) for igeom in geoms] if corr_dicts[0]["feats"] != []: - colors = _bmutils.matplotlib_colors_no_blue(ncycles=int(_np.ceil(_np.max(proj_idxs)/6.))) # Hack + colors = _bmutils.matplotlib_colors_no_blue(ncycles=int(_np.ceil((_np.max(proj_idxs)+1)/6.))) # Hack colors = [colors[ii] for ii in proj_idxs] + #colors = ['red']*10 # for the paper, to be deleted later else: n_feats=0 else: @@ -626,7 +634,6 @@ def traj(MD_trajectories, return mpx_wdg_box - def correlations(correlation_input, geoms=None, proj_idxs=None, @@ -643,7 +650,7 @@ def correlations(correlation_input, correlation_input : numpy ndarray or some PyEMMA objects - if array : + if array : (m,m) correlation matrix, with a row for each feature and a column for each projection if PyEMMA-object : @@ -701,7 +708,8 @@ def correlations(correlation_input, corr_dict and ngl_wdg corr_dict : - Dictionary with items: + Dictionary containing correlation information. For an overview, just issue `print(corr_dict)`. The + values are stored under the following keys. idxs : List of length len(proj_idxs) with lists of length n_feat with the idxs of the most correlated features @@ -761,14 +769,12 @@ def correlations(correlation_input, for ii, line in enumerate(corr_dict["info"]): print('%s is most correlated with '%(line["name"] )) for line in line["lines"]: - # TODO: this is for when tica is there but no featurizer is there if widget is not None and len(corr_dict["atom_idxs"]) != 0: line += ' (in %s in the widget)'%(proj_color_list[ii]) print(line) return corr_dict, widget - def feature(feat, widget, idxs=0, @@ -783,7 +789,7 @@ def feature(feat, ---------- featurizer : py:obj:`_MDFeautrizer` - A PyEMMA MDFeaturizer object (either a feature or a featurizer, works with both) + A PyEMMA MDfeaturizer object with any number of .active_features() widget : None or nglview widget Provide an already existing widget to visualize the correlations on top of. This is only for expert use, @@ -813,7 +819,7 @@ def feature(feat, """ idxs = _bmutils.listify_if_int(idxs) - atom_idxs = _bmutils.atom_idxs_from_feature(feat)[idxs] + atom_idxs = _bmutils.atom_idxs_from_general_input(feat)[slice(*idxs)] if color_list is None: color_list = ['blue'] * len(idxs) @@ -829,7 +835,6 @@ def feature(feat, return widget - def sample(positions, geom, ax, plot_path=False, clear_lines=True, @@ -931,9 +936,14 @@ def sample(positions, geom, ax, axes_wdg: :obj:`~matplotlib.Axes.AxesWidget` """ + # Make a copy of the geometry, otherwise the input gets destroyed + if isinstance(geom, list): + copy_geom = [gg[:] for gg in geom] + elif isinstance(geom, _md.Trajectory): + copy_geom = geom[:] if not sticky: - return _sample(positions, geom, ax, + return _sample(positions, copy_geom, ax, plot_path = plot_path, clear_lines = clear_lines, n_smooth = n_smooth, @@ -944,11 +954,11 @@ def sample(positions, geom, ax, **link_ax2wdg_kwargs) else: - if isinstance(geom, _md.Trajectory): - geom=[geom] + if isinstance(copy_geom, _md.Trajectory): + copy_geom=[copy_geom] # The method takes care of whatever superpose - geom = _bmutils.superpose_to_most_compact_in_list(superpose, geom) + copy_geom = _bmutils.superpose_to_most_compact_in_list(superpose, copy_geom) if color_list is None: sticky_colors_hex = ['Element' for ii in range(len(positions))] @@ -964,7 +974,7 @@ def sample(positions, geom, ax, raise TypeError('argument color_list should be either None, "random", or a list of len(pos)=%u, ' 'instead of type %s and len %u' % (len(positions), type(color_list), len(color_list))) sticky_rep = 'cartoon' - if geom[0].top.n_residues < 10: + if copy_geom[0].top.n_residues < 10: sticky_rep = 'ball+stick' if list_of_repr_dicts is None: list_of_repr_dicts = [{'repr_type': sticky_rep, 'selection': 'all'}] @@ -974,7 +984,7 @@ def sample(positions, geom, ax, # Prepare Geometry_in_widget_list ngl_wdg._GeomsInWid = [_linkutils.GeometryInNGLWidget(igeom, ngl_wdg, color_molecule_hex= cc, - list_of_repr_dicts=list_of_repr_dicts) for igeom, cc in zip(_bmutils.transpose_geom_list(geom), sticky_colors_hex)] + list_of_repr_dicts=list_of_repr_dicts) for igeom, cc in zip(_bmutils.transpose_geom_list(copy_geom), sticky_colors_hex)] axes_wdg = _linkutils.link_ax_w_pos_2_nglwidget(ax, positions, @@ -986,7 +996,6 @@ def sample(positions, geom, ax, return ngl_wdg, axes_wdg - def _sample(positions, geoms, ax, plot_path=False, clear_lines=True, @@ -1061,7 +1070,7 @@ def _sample(positions, geoms, ax, """ - assert isinstance(geoms, (list, _md.Trajectory)) + assert isinstance(geoms, (list, _md.Trajectory)), type(geoms) # Dow I need to smooth things out? if n_smooth > 0: @@ -1090,7 +1099,7 @@ def _sample(positions, geoms, ax, [ax.lines.pop() for ii in range(len(ax.lines))] # Plot the path on top of it if plot_path: - ax.plot(positions[:,0], positions[:,1], '-g', lw=3) + ax.plot(positions[:,0], positions[:,1], '-k', lw=3) # Link the axes ngl_wdg with the ngl ngl_wdg axes_wdg = _linkutils.link_ax_w_pos_2_nglwidget(ax, @@ -1114,3 +1123,269 @@ def _sample(positions, geoms, ax, return ngl_wdg, axes_wdg +def contacts(contact_map, input, residue_indices=None, average=False, panelsize=4): + r""" + Return a plot of the contact map and a linked :obj:`nglview.NGLWidget`. Clicking on + a pixel of interest on the contact map will a) highlight that pixel and b) + add lines in the widget ,connecting the corresponding atoms. Also, any updates in + the widget's frame, via the sliding bar, will update the shown contact map (in + case more than one contact map was provided) + + Parameters + ---------- + contact_map : square nd.array or iterable thereof. + These square arrays contain the contact map(s) + + input : :obj:`mdtraj.Trajectory` object or a list thereof. + An :obj:`nglview.NGLWidget` will be instantiated with this input + + residue_indices : boolean or iterable of integers + Residue indices corresponding to the :obj:`contact_map`. If None, an array + (0,1,...n_residues) will be created. + # TODO if not None, a NotImplementedError will be raised, because the relabeling of + zoomable plots is not yet implemented by molpx) + + average : boolean, default is False + Plot only the average of the contact maps provided in :obj:`contact_map`. If only one + such map is given, this keyword has no effect. If average is false but the + number of frames in :obj:`input` and :obj:`contact_map` don match, an exception is thrown. + + panelsize : int, default is 4 + The size of the figure and widget that will be outputted inside the molpxbox + + Returns + -------- + + mpxbox : An :obj:`nglview.NGLWidget` + """ + + from matplotlib import pyplot as _plt + # Add one axis to the input if necessary + if _np.ndim(contact_map)==2: + contact_map = _np.array(contact_map, ndmin=3) + + # Check that the number of frames match if no average is requested + if _np.ndim(contact_map)==3 and not average: + assert len(contact_map) == input.n_frames, "If average is False, the number of contact maps (%u) must " \ + "match the number of frames in input (%u)" % ( + len(contact_map), input.n_frames) + # Assert squaredness + assert all([ict.shape[0] == ict.shape[1] for ict in contact_map]), "The input has to be a square matrix" + + # Needed arrays + nres = contact_map[0].shape[0] + positions = _np.vstack(_np.unravel_index(range(nres**2), (nres,nres))).T + if residue_indices is None: + residue_pairs = positions + else: + raise NotImplementedError("This feature is not implemented yet!") + + # Create a color list + cmap = _get_cmap('rainbow') + cmap_table = _np.linspace(0, 1, len(positions)) + sticky_colors_hex = [_rgb2hex(cmap(ii)) for ii in _np.random.permutation(cmap_table)] + + # Instantiate widget + iwd = _nglwidget_wrapper(input) + + # Do the plot + _plt.ioff() + _plt.figure(figsize=(panelsize, panelsize)) + iax = _plt.gca() + # _plt.plot(positions[:,0], positions[:,1], ' ok') + # Make the average if wanted + if average: + iax.matshow(_np.average(contact_map, axis=0)) + else: + # Monkey-Patch the matshow_data into the object + iwd._MatshowData = {"image" : iax.matshow(contact_map[0]), + "data" : contact_map} + _plt.ion() + + + # TODO: if residues is not None, + # TODO make sure that zooming works even if a sub-set of res_idxs is given + """ + # Relabel the plot + for axtype in ['x', 'y']: + tic_idxs = [int(tl) for tl in getattr(iax, 'get_%sticks'%axtype)()[1:-1]] + tic_labels = ['']+['%u'%residue_idxs[ii] for ii in tic_idxs]+[''] + getattr(iax,'set_%sticklabels'%axtype)(tic_labels) + """ + + # Monkey-Patch the ContactInNGLWidgets into the widget + iwd._CtcsInWid = [_linkutils.ContactInNGLWidget + (iwd, [_bmutils.get_repr_atom_for_residue(input.top.residue(aa)).index for aa in [ii,jj]], rp_idx, + #verbose=True, + color= sticky_colors_hex[rp_idx] + ) + for rp_idx, (ii,jj) in enumerate(residue_pairs)] + + # Turn axes into a widget + axes_wdg = _linkutils.link_ax_w_pos_2_nglwidget(iax, + positions, + iwd, + crosshairs=False, + dot_color='None', + #**link_ax2wdg_kwargs + ) + + iwd._set_size(*['%fin' % inches for inches in iax.get_figure().get_size_inches()]) + #iax.figure.tight_layout() + axes_wdg.canvas.set_window_title("Contact Map") + outbox = _linkutils.MolPXHBox([iwd, axes_wdg.canvas]) + _linkutils.auto_append_these_mpx_attrs(outbox, input, iax, _plt.gcf(), iwd, axes_wdg, positions) + + return outbox + + +def MSM(msm_obj, traj_inp, + pos=None, + sharpen=False, + n_overlays=1, + top=None, + sticky=False, + panelsize=6, + **networkplot_kwargs): + + r""" + Visualize an MSM or an HMM as a network of nodes and egdes, together with an :obj:~`nglview.NGLWidget` + containing representative structures of node/state. Clicking on the node will update the widget. + + Parameters + ---------- + msm_obj: input MSM-object + One of PyEMMA's MSM-objects, either a "normal" MSM (:obj:`~pyemma.msm.MaximumLikelihoodMSM`) or a hidden MSM (:obj:`~pyemma.msm.MaximumLikelihoodHMSM`) + + traj_inp : trajectory input + Where to get the geometries from. It must be the same input with which the :obj:`msm_obj` was built. + No checks are done by the method as to whether this is true, i.e. *rubbish-in->rubbish-out*. + It can be of three different types (and lists thereof): + + * filenames (for which a :obj:`top` is needed, see below) + * :obj:`mdtraj.Trajectory` objects + * a PyEMMA's :obj:`~pyemma.coordinates.data.feature_reader.FeatureReader` + + pos : node positions, either None or a numpy ndarray of ndim=2 + By default, node positions are optimized to represent connectivity + (see PyEMMA's :obj:`~pyemma.plots.plot_markov_model`). However, the user can override with + custom node-positions by passing an array as :obj:`pos`. In many cases, it is useful for that array to be + the clustercenter-positions with which the MSM/HMM was constructed: :obj:`pos=cl.clustercenters`. + The input in :obj:`pos` has to be compatible with the provided :obj:`msm_obj`, i.e. have the necessary + number entries. The error messages will inform about what's wrong. + + sharpen : boolean, default is False, + This keyword only has effect for an HMM as an :obj:`input_msm`. + By default, the method samples from the distribution of microstate inside each macrostate, using the object's + :obj:`~pyemma.msm.MaximumLikelihoodHMSM.sample_by_observation_probabilities`- method. This can lead + to fuzzy samples where the overlay of molecular structures is not very informative. + + If :obj:`sharpen` is True, only the microstate that maximizes each macrostate's probabilites + (i.e., its argmax) will be sampled. Produces a more *sharpened* sample, + which is less representative of the whole set but very representative of the most probable + microstate within that set. + + n_overlays : int, default is 1 + Number of structures to represent simultaneously for each node of the network + + top : str or :obj:`mdtraj.Topology`, default is None + If the filenames in :obj:`traj_inp` need a topology, here's where you pass it along + + sticky : bool, default is False + Behaviour of the mouseclick when clicking a node in the network. + If True, left click adds-structures, right-click deletes them + + panelsize : int, default is 6 + Size of the network figure, in inches. If :obj:`pos` is provided, the panelsize will be adapted + slightly to match the proportions of :obj:`pos` + + networkplot_kwargs : named keyword arguments for :obj:`~pyemma.plots.plot_markov_model` + + Returns : + --------- + + mpxbox : A :obj:`~molpx.linkutils.MolPXHBox`-object. It contains the :obj:`nglview.NGLWidget` and the network + plot. Check the :obj:`mpxbox_linked*` attributes to see what the object contains + + """ + from pyemma.msm.estimators.maximum_likelihood_hmsm import MaximumLikelihoodHMSM as _MLHMSM + from pyemma.msm.estimators.maximum_likelihood_msm import MaximumLikelihoodMSM as _MLMSM + from pyemma.plots import plot_markov_model as _pyemma_plt_msm + from pyemma.util.discrete_trajectories import sample_indexes_by_state as _sample_indexes_by_state + from matplotlib import pyplot as _plt + + assert isinstance(msm_obj, (_MLHMSM, _MLMSM)), "Allowed input types are %s, not %s" % ((_MLHMSM, _MLMSM), type(MSM)) + + # Input parsing of the position object (#TODO reduce code?) + if pos is None: + pass + elif isinstance(pos, _np.ndarray): + if isinstance(msm_obj, _MLMSM): + assert len(pos) == msm_obj.nstates, \ + ("Number of input positions (%u) " + "does not match number %u active states of the MSM. Try slicing the input positions with " + "MSM.active_set" % (len(pos), msm_obj.nstates)) + elif isinstance(msm_obj, _MLHMSM): + assert len(pos) == msm_obj.nstates or len(pos) == msm_obj.nstates_obs, \ + ("With the input HMSM, the input positions have to have either " + "%u entries (number of coarse states in the HMSM) or %u entries (number of observed states of the HMSM). " + "You have provided neither: %u" % (msm_obj.nstates, msm_obj.nstates_obs, len(pos))) + else: + raise TypeError("The object_for_positions has the wrong type %s" % type(pos)) + + # MSM without coarse-graining + if isinstance(msm_obj, _MLMSM): + sample_frames = _np.vstack(msm_obj.sample_by_state(n_overlays)) + + # HMM + else: + if sharpen: + active_state_indexes = msm_obj.observable_state_indexes + subset = _np.argmax(msm_obj.observation_probabilities, axis=1) + sample_frames = _np.vstack(_sample_indexes_by_state(active_state_indexes, n_overlays, + subset=subset, replace=True)) + # The user gave one position entry per metastable set + if pos is not None and len(pos)==msm_obj.nstates: + pass # im leaving this case for clarity, in theory it could be removed + # The user gave a full array of positions that matches the total number of microstates + # and wants the method to choose automagically the positions that match the argmax(PDF) + elif pos is not None and len(pos)==msm_obj.nstates_obs: + pos = pos[subset] + # Any other case has ben caught before by the above ValueErrors + else: + sample_frames = _np.vstack(msm_obj.sample_by_observation_probabilities(n_overlays)) + # The user gave one position entry per metastable set + if pos is not None and len(pos)==msm_obj.nstates: + pass # im leaving this case for clarity, in theory it could be removed + # The user gave a full array of positions that matches the total number of microstates + # and wants the method to wheight them using the observation probabilities + elif pos is not None and len(pos)==msm_obj.nstates_obs: + isample_pos = [] + for idist in msm_obj.observation_probabilities: + isample_pos.append(_np.average(pos, weights=idist, axis=0)) + pos = _np.vstack(isample_pos) + + sample_geoms = _bmutils.save_traj_wrapper(traj_inp, sample_frames, None, top=top) + sample_geoms = _bmutils.re_warp(sample_geoms, n_overlays) + sample_geoms = _bmutils.transpose_geom_list(sample_geoms) + + _plt.ioff() + ifig, pos = _pyemma_plt_msm(msm_obj.P, pos=pos, + **networkplot_kwargs, + ) + # Conserve the proportion of the circles in the MSM plot + figw, figh = ifig.get_size_inches() + ifig.set_size_inches((panelsize, panelsize * figh / figw)) + iax = ifig.gca() + + ngl_wdg, axes_wdg = sample(pos, sample_geoms, iax, + sticky=sticky, + crosshairs=False, + )#, clear_lines=False, **sample_kwargs) + ngl_wdg._set_size(*['%fin' % inches for inches in ifig.get_size_inches()]) + ifig.tight_layout() + outbox = _linkutils.MolPXHBox([ngl_wdg, ifig.canvas]) + _linkutils.auto_append_these_mpx_attrs(outbox, sample_geoms, _plt.gca(), ifig, ngl_wdg, axes_wdg, pos) + _plt.ion() + + return outbox \ No newline at end of file