text: # coding=utf-8
  # Copyright 2018 The DisentanglementLib Authors.  All rights reserved.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """Utility functions that are useful for the different metrics."""
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import numpy as np
  from six.moves import range
  import sklearn
  from sklearn.ensemble import GradientBoostingClassifier
  from sklearn.linear_model import LogisticRegressionCV
  from sklearn.model_selection import KFold
  import gin.tf
  
  
  def generate_batch_factor_code(ground_truth_data, representation_function,
                                 num_points, random_state, batch_size):
    """Sample a single training sample based on a mini-batch of ground-truth data.
  
    Args:
      ground_truth_data: GroundTruthData to be sampled from.
      representation_function: Function that takes observation as input and
        outputs a representation.
      num_points: Number of points to sample.
      random_state: Numpy random state used for randomness.
      batch_size: Batchsize to sample points.
  
    Returns:
      representations: Codes (num_codes, num_points)-np array.
      factors: Factors generating the codes (num_factors, num_points)-np array.
    """
    representations = None
    factors = None
    i = 0
    while i < num_points:
      num_points_iter = min(num_points - i, batch_size)
      current_factors, current_observations = \
          ground_truth_data.sample(num_points_iter, random_state)
      if i == 0:
        factors = current_factors
        representations = representation_function(current_observations)
      else:
        factors = np.vstack((factors, current_factors))
        representations = np.vstack((representations,
                                     representation_function(
                                         current_observations)))
      i += num_points_iter
    return np.transpose(representations), np.transpose(factors)
  
  
  def split_train_test(observations, train_percentage):
    """Splits observations into a train and test set.
  
    Args:
      observations: Observations to split in train and test. They can be the
        representation or the observed factors of variation. The shape is
        (num_dimensions, num_points) and the split is over the points.
      train_percentage: Fraction of observations to be used for training.
  
    Returns:
      observations_train: Observations to be used for training.
      observations_test: Observations to be used for testing.
    """
    num_labelled_samples = observations.shape[1]
    num_labelled_samples_train = int(
        np.ceil(num_labelled_samples * train_percentage))
    num_labelled_samples_test = num_labelled_samples - num_labelled_samples_train
    observations_train = observations[:, :num_labelled_samples_train]
    observations_test = observations[:, num_labelled_samples_train:]
    assert observations_test.shape[1] == num_labelled_samples_test, \
        "Wrong size of the test set."
    return observations_train, observations_test
  
  
  
  
  def discrete_mutual_info(mus, ys):
    """Compute discrete mutual information."""
    num_codes = mus.shape[0]
    num_factors = ys.shape[0]
    m = np.zeros([num_codes, num_factors])
    for i in range(num_codes):
      for j in range(num_factors):
        m[i, j] = sklearn.metrics.mutual_info_score(ys[j, :], mus[i, :])
    return m
  
  
  def discrete_entropy(ys):
    """Compute discrete mutual information."""
    num_factors = ys.shape[0]
    h = np.zeros(num_factors)
    for j in range(num_factors):
      h[j] = sklearn.metrics.mutual_info_score(ys[j, :], ys[j, :])
    return h
  
  
  @gin.configurable(
      "discretizer", blacklist=["target"])
  def make_discretizer(target, num_bins=gin.REQUIRED,
                       discretizer_fn=gin.REQUIRED):
    """Wrapper that creates discretizers."""
    return discretizer_fn(target, num_bins)
  
  
  @gin.configurable("histogram_discretizer", blacklist=["target"])
  def _histogram_discretize(target, num_bins=gin.REQUIRED):
    """Discretization based on histograms."""
    discretized = np.zeros_like(target)
    for i in range(target.shape[0]):
      discretized[i, :] = np.digitize(target[i, :], np.histogram(
          target[i, :], num_bins)[1][:-1])
    return discretized
  
  
  def normalize_data(data, mean=None, stddev=None):
    if mean is None:
      mean = np.mean(data, axis=1)
    if stddev is None:
      stddev = np.std(data, axis=1)
    return (data - mean[:, np.newaxis]) / stddev[:, np.newaxis], mean, stddev
  
  
  @gin.configurable("predictor")
  def make_predictor_fn(predictor_fn=gin.REQUIRED):
    """Wrapper that creates classifiers."""
    return predictor_fn
  
  
  @gin.configurable("logistic_regression_cv")
  def logistic_regression_cv():
    """Logistic regression with 5 folds cross validation."""
    return LogisticRegressionCV(Cs=10, cv=KFold(n_splits=5))
  
  
  @gin.configurable("gradient_boosting_classifier")
  def gradient_boosting_classifier():
    """Default gradient boosting classifier."""
    return GradientBoostingClassifier()
  