Example #1
0
 def _compute_probability_distribution_eigenvector(self):
     """
     Find the eigenvector (s) of the transition matrix
     :param transition_count:
     :return:
     """
     transition_probability = np.zeros(self.transition_count.shape)
     transition_count = self._remove_transitions_to_isolated_bins(
         self.transition_count)
     for rowidx, row in enumerate(transition_count):
         # transition_probability[rowidx, rowidx] = 0
         rowsum = np.sum(row)
         if rowsum > 0:
             transition_probability[rowidx] = row / rowsum
     eigenvalues, eigenvectors = np.linalg.eig(transition_probability.T)
     stationary_solution = None
     unit_eigenval = None  # The eigenvalue closest to 1
     for idx, eigenval in enumerate(eigenvalues):
         vec = eigenvectors[:, idx]
         # logger.debug("Eigenvec for eigenvalue %s:\n%s", eigenval, vec)
         if np.isclose(1.0, eigenval, rtol=1e-2):
             neg_vec, pos_vec = vec[vec < 0], vec[vec > 0]
             if len(pos_vec) == 0:
                 # No positive entries. All must be negative. We can multiply the eigenvector by a factor of -1
                 vec = -1 * vec
             elif len(neg_vec) > 0:
                 logger.warning(
                     "Found a vector with eigenvalue ~1(%s) but with negative entries in its eigenvector",
                     eigenval,
                 )
                 continue
             if stationary_solution is not None:
                 raise Exception(
                     "Multiple stationary solutions found. Perhaps there were no transitions between states. Eigenvalues:\n%s"
                     % eigenvalues)
             vec = np.real(vec)
             stationary_solution = vec / np.sum(vec)
             unit_eigenval = eigenval
     relaxation_eigenval = (
         None  # corresponds to the largest eigenvalue less than 1
     )
     for idx, eigenval in enumerate(eigenvalues):
         if eigenval < 1 and eigenval != unit_eigenval:
             if (relaxation_eigenval is None
                     or eigenval > relaxation_eigenval):
                 relaxation_eigenval = eigenval
     if stationary_solution is None:
         raise Exception("No stationary solution found. Eigenvalues:\n%s",
                         eigenvalues)
     if relaxation_eigenval is not None:
         logger.info(
             "Relaxation time for system: %s [units of lag time]. Eigenval=%s",
             -np.log(relaxation_eigenval),
             relaxation_eigenval,
         )
     return stationary_solution
Example #2
0
 def compute_transition_count(self) -> np.array:
     n_cvs = self.cv_coordinates.shape[2]
     nbins = self.n_grid_points**n_cvs
     transition_count = np.zeros((nbins, nbins))  # Transition per bin
     for t in self.cv_coordinates:
         if np.any(np.isnan(t) | np.isinf(t)):
             logger.warning("Found NaN or Inf transition. Ignoring it.")
             continue
         start_grid = self._find_grid_coordinates(t[0])
         end_grid = self._find_grid_coordinates(t[-1])
         start_bin = self._index_converter.convert_to_bin_idx(start_grid)
         end_bin = self._index_converter.convert_to_bin_idx(end_grid)
         transition_count[start_bin, end_bin] += 1
     return transition_count
Example #3
0
 def _remove_transitions_to_isolated_bins(self, transition_count):
     """Remove all transitions which moves from a bin with no starting points"""
     last_inaccessible_states, last_nonstarting_states = -1, -1
     inaccessible_states, nonstarting_states = 1, 1
     while (inaccessible_states != last_inaccessible_states
            or nonstarting_states != last_nonstarting_states):
         last_inaccessible_states, last_nonstarting_states = (
             inaccessible_states,
             nonstarting_states,
         )
         inaccessible_states, nonstarting_states, accessible_states = (
             0,
             0,
             0,
         )
         for rowidx, row in enumerate(transition_count):
             rowsum = np.sum(row)
             if rowsum == 0:
                 # transition_probability[rowidx] = 0
                 if np.sum(transition_count[:, rowidx]) == 0:
                     # logger.warning("Found inaccessible state at index %s ", rowidx)
                     inaccessible_states += 1
                     # rho[rowidx] = np.nan
                 else:
                     # logger.warning("Found non-starting states")
                     nonstarting_states += 1
                 # TODO see if this makes sense: to set all transition into this state to zero to completely isolate it!
                 transition_count[:, rowidx] = 0
             else:
                 accessible_states += 1
     if inaccessible_states > 0 or nonstarting_states > 0:
         logger.warning(
             "Found %s accessible states, %s inaccessible states and %s states with no starting points.",
             accessible_states,
             inaccessible_states,
             nonstarting_states,
         )
     return transition_count
Example #4
0
    def _compute_probability_distribution_detailed_balance(self):
        nbins = self.transition_count.shape[0]
        transition_probability = np.zeros(self.transition_count.shape)
        rho = np.zeros((nbins, ))
        inaccessible_states, nonstarting_states = 0, 0
        transition_count = self._remove_transitions_to_isolated_bins(
            self.transition_count)
        for rowidx, row in enumerate(transition_count):
            # transition_probability[rowidx, rowidx] = 0
            rowsum = np.sum(row)
            if rowsum > 0:
                # transition_probability[rowidx, rowidx] = 0
                transition_probability[rowidx] = row / rowsum
                # transition_probability[rowidx, rowidx] = -rowsum
            else:
                rho[rowidx] = np.nan

        if inaccessible_states > 0 or nonstarting_states > 0:
            logger.warning(
                "Found %s inaccessible states and %s states with no starting points.",
                inaccessible_states,
                nonstarting_states,
            )
        # Set up starting guess for distribution: all accessible states equal
        for i, rhoi in enumerate(rho):
            if np.isnan(rhoi):
                rho[i] = 0
            else:
                rho[i] = 1
        rho = rho / np.sum(rho)
        convergences = []
        convergence = 100
        while convergence > 1e-6:
            last = rho  # np.copy(rho)
            for k, rhok in enumerate(rho):
                if rhok == 0:
                    continue
                crossterm = np.dot(rho, transition_probability[:, k]) - np.sum(
                    rhok * transition_probability[k, :])
                # crossterm = 0
                # for l, rhol in enumerate(rho):
                #     if l != k:
                #         crossterm += rhol * transition_probability[l, k] - rhok * transition_probability[k, l]
                if rhok == 0 and crossterm > 0:
                    logger.warning(
                        "NOOOO for index %s. Crossterm %s rhok %s",
                        k,
                        crossterm,
                        rhok,
                    )
                rho[k] = rhok + crossterm
            rho = rho / np.sum(rho)
            if last is not None:
                convergence = np.linalg.norm(rho - last)
                convergences.append(convergence)
        logger.debug(
            "Converged with master equation after %s iterations",
            len(convergences),
        )
        # plt.plot(convergences, label="Convergence")
        # plt.show()
        if len(rho[rho == 0]) < inaccessible_states:
            raise Exception(
                "Something went wrong. Number inaccessible states differ %s vs. %s"
                % (len(rho[rho == 0]), inaccessible_states))
        return rho