code: # -*- coding: utf-8 -*-
  
  """
  The :mod:`coclust.coclustering.coclust_mod` module provides an implementation
  of a co-clustering algorithm by direct maximization of graph modularity.
  """
  
  # Author: Francois Role <francois.role@gmail.com>
  #         Stanislas Morbieu <stanislas.morbieu@gmail.com>
  
  # License: BSD 3 clause
  
  import numpy as np
  from sklearn.utils import check_random_state, check_array
  from joblib import Parallel, delayed, effective_n_jobs
  
  from ..initialization import random_init
  from .base_diagonal_coclust import BaseDiagonalCoclust
  
  
  def _fit_single(X, n_clusters, random_state, init, max_iter, tol, y=None):
          """Perform one run of co-clustering by direct maximization of graph
          modularity.
  
          Parameters
          ----------
          X : numpy array or scipy sparse matrix, shape=(n_samples, n_features)
              Matrix to be analyzed
          """
          if init is None:
              W = random_init(n_clusters, X.shape[1], random_state)
          else:
              W = np.matrix(init, dtype=float)
  
          Z = np.zeros((X.shape[0], n_clusters))
  
          # Compute the modularity matrix
          row_sums = np.matrix(X.sum(axis=1))
          col_sums = np.matrix(X.sum(axis=0))
          N = float(X.sum())
          indep = (row_sums.dot(col_sums)) / N
  
          # B is a numpy matrix
          B = X - indep
  
          modularities = []
  
          # Loop
          m_begin = float("-inf")
          change = True
          iteration = 0
          while change:
              change = False
  
              # Reassign rows
              BW = B.dot(W)
              for idx, k in enumerate(np.argmax(BW, axis=1)):
                  Z[idx, :] = 0
                  Z[idx, k] = 1
  
              # Reassign columns
              BtZ = (B.T).dot(Z)
              for idx, k in enumerate(np.argmax(BtZ, axis=1)):
                  W[idx, :] = 0
                  W[idx, k] = 1
  
              k_times_k = (Z.T).dot(BW)
              m_end = np.trace(k_times_k)
              iteration += 1
              if (np.abs(m_end - m_begin) > tol and
                      iteration < max_iter):
                  modularities.append(m_end/N)
                  m_begin = m_end
                  change = True
  
          row_labels_ = np.argmax(Z, axis=1).tolist()
          column_labels_ = np.argmax(W, axis=1).tolist()
          modularity = m_end / N
          nb_iterations = iteration
          return row_labels_,  column_labels_, modularity, modularities, nb_iterations
  
  
  class CoclustMod(BaseDiagonalCoclust):
      """Co-clustering by direct maximization of graph modularity.
  
      Parameters
      ----------
      n_clusters : int, optional, default: 2
          Number of co-clusters to form
  
      init : numpy array or scipy sparse matrix, \
          shape (n_features, n_clusters), optional, default: None
          Initial column labels
  
      max_iter : int, optional, default: 20
          Maximum number of iterations
  
      n_init : int, optional, default: 1
          Number of time the algorithm will be run with different
          initializations. The final results will be the best output of `n_init`
          consecutive runs in terms of modularity.
  
      random_state : integer or numpy.RandomState, optional
          The generator used to initialize the centers. If an integer is
          given, it fixes the seed. Defaults to the global numpy random
          number generator.
  
      tol : float, default: 1e-9
          Relative tolerance with regards to modularity to declare convergence
  
      Attributes
      ----------
      row_labels_ : array-like, shape (n_rows,)
          Bicluster label of each row
  
      column_labels_ : array-like, shape (n_cols,)
          Bicluster label of each column
  
      modularity : float
          Final value of the modularity
  
      modularities : list
          Record of all computed modularity values for all iterations
  
      References
      ----------
      * Ailem M., Role F., Nadif M., Co-clustering Document-term Matrices by \
      Direct Maximization of Graph Modularity. CIKM 2015: 1807-1810
      """
  
      def __init__(self, n_clusters=2, init=None, max_iter=20, n_init=1,
                   tol=1e-9, random_state=None, n_jobs=1):
          self.n_clusters = n_clusters
          self.init = init
          self.max_iter = max_iter
          self.n_init = n_init
          self.tol = tol
          self.random_state = random_state
          self.n_jobs = n_jobs
          # to remove except for self.modularity = -np.inf!!!
          self.row_labels_ = None
          self.column_labels_ = None
          self.modularity = -np.inf
          self.modularities = []
  
  
  
      def fit(self, X, y=None):
          """Perform co-clustering by direct maximization of graph modularity.
  
          Parameters
          ----------
          X : numpy array or scipy sparse matrix, shape=(n_samples, n_features)
              Matrix to be analyzed
          """
  
          random_state = check_random_state(self.random_state)
  
          check_array(X, accept_sparse=True, dtype="numeric", order=None,
                      copy=False, force_all_finite=True, ensure_2d=True,
                      allow_nd=False, ensure_min_samples=self.n_clusters,
                      ensure_min_features=self.n_clusters,
                      warn_on_dtype=False, estimator=None)
  
          if type(X) == np.ndarray:
              X = np.matrix(X)
  
          X = X.astype(float)
  
          modularity = self.modularity
          modularities = []
          row_labels = None
          column_labels = None
          seeds = random_state.randint(np.iinfo(np.int32).max, size=self.n_init)
          if effective_n_jobs(self.n_jobs) == 1:
           for seed in seeds:
              new_row_labels,  new_column_labels, new_modularity, new_modularities, new_nb_iterations = _fit_single(X, self.n_clusters, seed, self.init, self.max_iter, self.tol, y)
              if np.isnan(new_modularity):
                  raise ValueError("matrix may contain unexpected NaN values")
              # remember attributes corresponding to the best modularity
              if (new_modularity > modularity):
                  modularity = new_modularity
                  modularities = new_modularities
                  row_labels = new_row_labels
                  column_labels = new_column_labels
          else:
           results = Parallel(n_jobs=self.n_jobs, verbose=0)(
              delayed(_fit_single)(X, self.n_clusters, seed, self.init, self.max_iter, self.tol, y)
              for seed in seeds)
           list_of_row_labels,  list_of_column_labels, list_of_modularity, list_of_modularities, list_of_nb_iterations = zip(*results)
           best = np.argmax(list_of_modularity)
           row_labels = list_of_row_labels[best]
           column_labels = list_of_column_labels[best]
           modularity = list_of_modularity[best]
           modularities = list_of_modularities[best]
           n_iter = list_of_nb_iterations[best]
  
           
  
          # update instance variables
          self.modularity = modularity
          self.modularities = modularities
          self.row_labels_ = row_labels
          self.column_labels_ = column_labels
  
          return self
  
apis: sklearn.utils.check_random_state numpy.zeros numpy.matrix sklearn.utils.check_array numpy.abs numpy.argmax numpy.trace numpy.iinfo numpy.isnan