# Path: hep_ml/commonutils.py
# def sigmoid_function(x, width):
#     """ Sigmoid function is smoothing of Heaviside function,
#     the less width, the closer we are to Heaviside function
#     :type x: array-like with floats, arbitrary shape
#     :type width: float, if width == 0, this is simply Heaviside function
#     """
#     assert width >= 0, 'the width should be non-negative'
#     if abs(width) > 0.0001:
#         return expit(x / width)
#     else:
#         return (x > 0) * 1.0
# 
# def check_sample_weight(y_true, sample_weight):
#     """
#     Checks the weights, returns normalized version
#     :param y_true: numpy.array of shape [n_samples]
#     :param sample_weight: array-like of shape [n_samples] or None
#     :returns: numpy.array with weights of shape [n_samples]"""
#     if sample_weight is None:
#         return numpy.ones(len(y_true), dtype=numpy.float)
#     else:
#         sample_weight = numpy.array(sample_weight, dtype=numpy.float)
#         assert len(y_true) == len(sample_weight), \
#             "The length of weights is different: not {0}, but {1}".format(len(y_true), len(sample_weight))
#         return sample_weight


from math import sqrt
from sklearn.base import BaseEstimator, ClassifierMixin, clone
from sklearn.utils.validation import column_or_1d
from .commonutils import sigmoid_function, check_sample_weight
import numpy
import pandas
        X = self.get_train_vars(X)
        score = numpy.zeros(len(X))
        for clf in self.estimators:
            score += self.learning_rate * self.compute_score(clf, X)
            yield score

    def predict_score(self, X):
        X = self.get_train_vars(X)
        return self.learning_rate * sum(self.compute_score(clf, X) for clf in self.estimators)

    def score_to_proba(self, score):
        """Compute class probability estimates from decision scores."""
        proba = numpy.zeros((score.shape[0], 2), dtype=numpy.float)
        proba[:, 1] = sigmoid_function(score, sqrt(self.n_estimators))
        proba[:, 0] = 1.0 - proba[:, 1]
        return proba

    @staticmethod
    def compute_score(clf, X):
        """X should include only train vars"""
        p = clf.predict_proba(X)
        p += 1e-5
        return numpy.log(p[:, 1] / p[:, 0])

    def get_train_vars(self, X):
        """Gets the DataFrame and returns only columns
           that should be used in fitting / predictions"""
        if self.train_variables is None:
            return X
        else:

# Path: hep_ml/commonutils.py
# def sigmoid_function(x, width):
#     """ Sigmoid function is smoothing of Heaviside function,
#     the less width, the closer we are to Heaviside function
#     :type x: array-like with floats, arbitrary shape
#     :type width: float, if width == 0, this is simply Heaviside function
#     """
#     assert width >= 0, 'the width should be non-negative'
#     if abs(width) > 0.0001:
#         return expit(x / width)
#     else:
#         return (x > 0) * 1.0
# 
# def check_sample_weight(y_true, sample_weight):
#     """
#     Checks the weights, returns normalized version
#     :param y_true: numpy.array of shape [n_samples]
#     :param sample_weight: array-like of shape [n_samples] or None
#     :returns: numpy.array with weights of shape [n_samples]"""
#     if sample_weight is None:
#         return numpy.ones(len(y_true), dtype=numpy.float)
#     else:
#         sample_weight = numpy.array(sample_weight, dtype=numpy.float)
#         assert len(y_true) == len(sample_weight), \
#             "The length of weights is different: not {0}, but {1}".format(len(y_true), len(sample_weight))
#         return sample_weight


# Path: hep_ml/supplementaryclassifiers.py
from math import sqrt
from sklearn.base import BaseEstimator, ClassifierMixin, clone
from sklearn.utils.validation import column_or_1d
from .commonutils import sigmoid_function, check_sample_weight
import numpy
import pandas

        X = self.get_train_vars(X)
        score = numpy.zeros(len(X))
        for clf in self.estimators:
            score += self.learning_rate * self.compute_score(clf, X)
            yield score

    def predict_score(self, X):
        X = self.get_train_vars(X)
        return self.learning_rate * sum(self.compute_score(clf, X) for clf in self.estimators)

    def score_to_proba(self, score):
        """Compute class probability estimates from decision scores."""
        proba = numpy.zeros((score.shape[0], 2), dtype=numpy.float)
        proba[:, 1] = sigmoid_function(score, sqrt(self.n_estimators))
        proba[:, 0] = 1.0 - proba[:, 1]
        return proba

    @staticmethod
    def compute_score(clf, X):
        """X should include only train vars"""
        p = clf.predict_proba(X)
        p += 1e-5
        return numpy.log(p[:, 1] / p[:, 0])

    def get_train_vars(self, X):
        """Gets the DataFrame and returns only columns
           that should be used in fitting / predictions"""
        if self.train_variables is None:
            return X
        else:

            return X[self.train_variables]