nodes: 0, module; 1, function_definition; 2, function_name:_opt_soft; 3, parameters; 4, block; 5, identifier:eigvectors; 6, identifier:rot_matrix; 7, identifier:n_clusters; 8, expression_statement; 9, comment:# only consider first n_clusters eigenvectors; 10, expression_statement; 11, comment:# crop first row and first column from rot_matrix; 12, comment:# rot_crop_matrix = rot_matrix[1:,1:]; 13, expression_statement; 14, expression_statement; 15, comment:# reshape rot_crop_matrix into linear vector; 16, expression_statement; 17, comment:# Susanna Roeblitz' target function for optimization; 18, function_definition; 19, import_from_statement; 20, expression_statement; 21, expression_statement; 22, expression_statement; 23, return_statement; 24, comment:"""
      Optimizes the PCCA+ rotation matrix such that the memberships are exclusively nonnegative.
  
      Parameters
      ----------
      eigenvectors : ndarray
          A matrix with the sorted eigenvectors in the columns. The stationary eigenvector should
          be first, then the one to the slowest relaxation process, etc.
  
      rot_mat : ndarray (m x m)
          nonoptimized rotation matrix
  
      n_clusters : int
          Number of clusters to group to.
  
      Returns
      -------
      rot_mat : ndarray (m x m)
          Optimized rotation matrix that rotates the dominant eigenvectors to yield the PCCA memberships, i.e.:
          chi = np.dot(evec, rot_matrix
  
      References
      ----------
      [1] S. Roeblitz and M. Weber, Fuzzy spectral clustering by PCCA+:
          application to Markov state models and data classification.
          Adv Data Anal Classif 7, 147-179 (2013).
  
      """; 25, assignment; 26, assignment; 27, assignment; 28, assignment; 29, function_name:susanna_func; 30, parameters; 31, comment:# reshape into matrix; 32, block; 33, dotted_name; 34, dotted_name; 35, assignment; 36, assignment; 37, assignment; 38, identifier:rot_matrix; 39, identifier:eigvectors; 40, subscript; 41, identifier:rot_crop_matrix; 42, subscript; 43, tuple_pattern; 44, attribute; 45, identifier:rot_crop_vec; 46, call; 47, identifier:rot_crop_vec; 48, identifier:eigvectors; 49, expression_statement; 50, comment:# fill matrix; 51, expression_statement; 52, expression_statement; 53, for_statement; 54, return_statement; 55, identifier:scipy; 56, identifier:optimize; 57, identifier:fmin; 58, identifier:rot_crop_vec_opt; 59, call; 60, identifier:rot_crop_matrix; 61, call; 62, identifier:rot_matrix; 63, call; 64, identifier:eigvectors; 65, slice; 66, slice; 67, subscript; 68, slice; 69, slice; 70, identifier:x; 71, identifier:y; 72, identifier:rot_crop_matrix; 73, identifier:shape; 74, attribute; 75, argument_list; 76, assignment; 77, assignment; 78, assignment; 79, identifier:i; 80, call; 81, block; 82, unary_operator; 83, identifier:fmin; 84, argument_list; 85, attribute; 86, argument_list; 87, identifier:_fill_matrix; 88, argument_list; 89, identifier:n_clusters; 90, identifier:rot_matrix; 91, slice; 92, integer:1; 93, identifier:np; 94, identifier:reshape; 95, identifier:rot_crop_matrix; 96, binary_operator:x * y; 97, identifier:rot_crop_matrix; 98, call; 99, identifier:rot_matrix; 100, call; 101, identifier:result; 102, integer:0; 103, identifier:range; 104, argument_list; 105, for_statement; 106, identifier:result; 107, identifier:susanna_func; 108, identifier:rot_crop_vec; 109, keyword_argument; 110, keyword_argument; 111, identifier:np; 112, identifier:reshape; 113, identifier:rot_crop_vec_opt; 114, tuple; 115, identifier:rot_crop_matrix; 116, identifier:eigvectors; 117, integer:1; 118, identifier:x; 119, identifier:y; 120, attribute; 121, argument_list; 122, identifier:_fill_matrix; 123, argument_list; 124, integer:0; 125, identifier:n_clusters; 126, identifier:j; 127, call; 128, block; 129, identifier:args; 130, tuple; 131, identifier:disp; 132, False; 133, identifier:x; 134, identifier:y; 135, identifier:np; 136, identifier:reshape; 137, identifier:rot_crop_vec; 138, tuple; 139, identifier:rot_crop_matrix; 140, identifier:eigvectors; 141, identifier:range; 142, argument_list; 143, expression_statement; 144, identifier:eigvectors; 145, identifier:x; 146, identifier:y; 147, integer:0; 148, identifier:n_clusters; 149, augmented_assignment; 150, identifier:result; 151, binary_operator:np.power(rot_matrix[j, i], 2) / rot_matrix[0, i]; 152, call; 153, subscript; 154, attribute; 155, argument_list; 156, identifier:rot_matrix; 157, integer:0; 158, identifier:i; 159, identifier:np; 160, identifier:power; 161, subscript; 162, integer:2; 163, identifier:rot_matrix; 164, identifier:j; 165, identifier:i
code: def _opt_soft(eigvectors, rot_matrix, n_clusters):
      """
      Optimizes the PCCA+ rotation matrix such that the memberships are exclusively nonnegative.
  
      Parameters
      ----------
      eigenvectors : ndarray
          A matrix with the sorted eigenvectors in the columns. The stationary eigenvector should
          be first, then the one to the slowest relaxation process, etc.
  
      rot_mat : ndarray (m x m)
          nonoptimized rotation matrix
  
      n_clusters : int
          Number of clusters to group to.
  
      Returns
      -------
      rot_mat : ndarray (m x m)
          Optimized rotation matrix that rotates the dominant eigenvectors to yield the PCCA memberships, i.e.:
          chi = np.dot(evec, rot_matrix
  
      References
      ----------
      [1] S. Roeblitz and M. Weber, Fuzzy spectral clustering by PCCA+:
          application to Markov state models and data classification.
          Adv Data Anal Classif 7, 147-179 (2013).
  
      """
      # only consider first n_clusters eigenvectors
      eigvectors = eigvectors[:, :n_clusters]
  
      # crop first row and first column from rot_matrix
      # rot_crop_matrix = rot_matrix[1:,1:]
      rot_crop_matrix = rot_matrix[1:][:, 1:]
  
      (x, y) = rot_crop_matrix.shape
  
      # reshape rot_crop_matrix into linear vector
      rot_crop_vec = np.reshape(rot_crop_matrix, x * y)
  
      # Susanna Roeblitz' target function for optimization
      def susanna_func(rot_crop_vec, eigvectors):
          # reshape into matrix
          rot_crop_matrix = np.reshape(rot_crop_vec, (x, y))
          # fill matrix
          rot_matrix = _fill_matrix(rot_crop_matrix, eigvectors)
  
          result = 0
          for i in range(0, n_clusters):
              for j in range(0, n_clusters):
                  result += np.power(rot_matrix[j, i], 2) / rot_matrix[0, i]
          return -result
  
      from scipy.optimize import fmin
  
      rot_crop_vec_opt = fmin(susanna_func, rot_crop_vec, args=(eigvectors,), disp=False)
  
      rot_crop_matrix = np.reshape(rot_crop_vec_opt, (x, y))
      rot_matrix = _fill_matrix(rot_crop_matrix, eigvectors)
  
      return rot_matrix